From 6cad7a7e29eb171afd9715a0ccd2226f6db3007b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 7 Jun 2022 14:46:19 +0200 Subject: [PATCH 001/312] Implement exponentiation for 4x4 sector --- src/eko/anomalous_dimensions/__init__.py | 48 ++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index 76f65ab4a..9561380f1 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -165,3 +165,51 @@ def gamma_singlet(order, n, nf): sx = np.append(sx, harmonics.S4(n)) gamma_s[2] = -as3.gamma_singlet(n, nf, sx) return gamma_s + + +@nb.njit(cache=True) +def exp_4x4_sector(gamma_S): + r""" + Computes the exponential and the eigensystem of the singlet anomalous dimension matrix + + Parameters + ---------- + gamma_S : numpy.ndarray + singlet anomalous dimension matrix + + Returns + ------- + exp : numpy.ndarray + exponential of the singlet anomalous dimension matrix :math:`\gamma_{S}(N)` + lambda_p : complex + positive eigenvalue of the singlet anomalous dimension matrix + :math:`\gamma_{S}(N)` + lambda_m : complex + negative eigenvalue of the singlet anomalous dimension matrix + :math:`\gamma_{S}(N)` + e_p : numpy.ndarray + projector for the positive eigenvalue of the singlet anomalous + dimension matrix :math:`\gamma_{S}(N)` + e_m : numpy.ndarray + projector for the negative eigenvalue of the singlet anomalous + dimension matrix :math:`\gamma_{S}(N)` + + See Also + -------- + eko.anomalous_dimensions.as1.gamma_singlet : :math:`\gamma_{S}^{(0)}(N)` + eko.anomalous_dimensions.as2.gamma_singlet : :math:`\gamma_{S}^{(1)}(N)` + eko.anomalous_dimensions.as3.gamma_singlet : :math:`\gamma_{S}^{(2)}(N)` + """ + # compute Matrix of coefficients + w, v = np.linalg.eig(gamma_S) + V = np.transpose(v).dot(v) + C = np.linalg.inv(V) + # compute projectors + tmp = C.dot(np.transpose(v)) + e1 = v[:, 0, np.newaxis].dot(tmp[np.newaxis, 0, :]) + e2 = v[:, 1, np.newaxis].dot(tmp[np.newaxis, 1, :]) + e3 = v[:, 2, np.newaxis].dot(tmp[np.newaxis, 2, :]) + e4 = v[:, 3, np.newaxis].dot(tmp[np.newaxis, 3, :]) + e = np.array([e1, e2, e3, e4]) + exp = sum(e[i] * np.exp(w[i]) for i in range(4)) + return exp, w, e From 322e7128e3b45e547faa425ba5a2da6e00a78219 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 5 Jul 2022 15:06:50 +0200 Subject: [PATCH 002/312] Implement gamma_singlet at O(a) --- src/eko/anomalous_dimensions/aem1.py | 35 +++++++++++++++++++++++++++- tests/eko/test_ad_aem1.py | 8 ++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/eko/anomalous_dimensions/aem1.py b/src/eko/anomalous_dimensions/aem1.py index b774d1ca1..50540d885 100644 --- a/src/eko/anomalous_dimensions/aem1.py +++ b/src/eko/anomalous_dimensions/aem1.py @@ -4,6 +4,7 @@ """ import numba as nb +import numpy as np from .. import constants from . import as1 @@ -72,7 +73,9 @@ def gamma_phph(nf): Leading-order phton-photon anomalous dimension :math:`\\gamma_{\\gamma \\gamma}^{(0)}(N)` """ - return 2 / 3 * constants.NC * 2 * nf + nu = constants.uplike_flavors(nf) + nd = nf - nu + return 4 / 3 * constants.NC * (nu * constants.eu2 + nd * constants.ed2) @nb.njit(cache=True) @@ -95,3 +98,33 @@ def gamma_ns(N, s1): Leading-order non-singlet QED anomalous dimension :math:`\\gamma_{ns}^{(0)}(N)` """ return as1.gamma_ns(N, s1) / constants.CF + + +@nb.njit(cache=True) +def gamma_singlet(N, s1, nf): + nu = constants.uplike_flavors(nf) + nd = nf - nu + vu = nu / nf + vd = nd / nf + e2avg = (nu * constants.eu2 + nd * constants.ed2) / nf + e2m = constants.eu2 - constants.ed2 + e2delta = (nd * constants.eu2 + nu * constants.ed2) / nf + gamma_S_01 = np.array( + [ + [0, 0, 0, 0], + [0, gamma_phph(nf), e2avg * gamma_phq(N), vu * e2m * gamma_phq(N)], + [ + 0, + e2avg * gamma_qph(N, nf), + e2avg * gamma_ns(N, s1), + vu * e2m * gamma_ns(N, s1), + ], + [ + 0, + vd * e2m * gamma_qph(N, nf), + vd * e2m * gamma_ns(N, s1), + e2delta * gamma_ns(N, s1), + ], + ] + ) + return gamma_S_01 diff --git a/tests/eko/test_ad_aem1.py b/tests/eko/test_ad_aem1.py index edf6b6186..aef86c822 100644 --- a/tests/eko/test_ad_aem1.py +++ b/tests/eko/test_ad_aem1.py @@ -3,6 +3,7 @@ import numpy as np from eko import anomalous_dimensions as ad +from eko import constants def test_number_conservation(): @@ -26,6 +27,11 @@ def test_photon_momentum_conservation(): # photon momentum N = complex(2.0, 0.0) for NF in range(2, 6 + 1): + NU = constants.uplike_flavors(NF) + ND = NF - NU np.testing.assert_almost_equal( - ad.aem1.gamma_qph(N, NF) + ad.aem1.gamma_phph(NF), 0 + constants.eu2 * ad.aem1.gamma_qph(N, NU) + + constants.ed2 * ad.aem1.gamma_qph(N, ND) + + ad.aem1.gamma_phph(NF), + 0, ) From d6e573e23d795c8feff518b4fba724a1a62cc466 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 5 Jul 2022 15:47:44 +0200 Subject: [PATCH 003/312] Implement gamma_singlet at O(a_s*a) --- src/eko/anomalous_dimensions/as1aem1.py | 41 +++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/eko/anomalous_dimensions/as1aem1.py b/src/eko/anomalous_dimensions/as1aem1.py index 1db1764ce..9ae141d44 100644 --- a/src/eko/anomalous_dimensions/as1aem1.py +++ b/src/eko/anomalous_dimensions/as1aem1.py @@ -4,6 +4,7 @@ """ import numba as nb +import numpy as np from .. import constants, harmonics from ..harmonics.constants import zeta2, zeta3 @@ -355,3 +356,43 @@ def gamma_nsm(N, sx): - 16 * zeta3 ) return constants.CF * result + + +@nb.njit(cache=True) +def gamma_singlet(N, s1, nf): + nu = constants.uplike_flavors(nf) + nd = nf - nu + vu = nu / nf + vd = nd / nf + e2avg = (nu * constants.eu2 + nd * constants.ed2) / nf + e2m = constants.eu2 - constants.ed2 + e2delta = (nd * constants.eu2 + nu * constants.ed2) / nf + gamma_S_01 = np.array( + [ + [ + nf * e2avg * gamma_gg(), + nf * e2avg * gamma_gph(N), + nf * e2avg * gamma_gq(N, s1), + vu * e2m * gamma_gq(N, s1), + ], + [ + nf * e2avg * gamma_phg(N), + gamma_phph(nf), + e2avg * gamma_phq(N), + vu * e2m * gamma_phq(N), + ], + [ + e2avg * gamma_qg(N, nf, s1), + e2avg * gamma_qph(N, nf), + e2avg * gamma_nsp(N, s1), + vu * e2m * gamma_nsp(N, s1), + ], + [ + vd * e2m * gamma_gq(N, s1), + vd * e2m * gamma_qph(N, nf), + vd * e2m * gamma_nsp(N, s1), + e2delta * gamma_nsp(N, s1), + ], + ] + ) + return gamma_S_01 From fb6599a6b4731a677468d4d020a9b9ede8577768 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 5 Jul 2022 16:22:02 +0200 Subject: [PATCH 004/312] Init QED 4x4 solution --- src/eko/kernels/QEDsinglet.py | 64 +++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 src/eko/kernels/QEDsinglet.py diff --git a/src/eko/kernels/QEDsinglet.py b/src/eko/kernels/QEDsinglet.py new file mode 100644 index 000000000..e8df7d143 --- /dev/null +++ b/src/eko/kernels/QEDsinglet.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +import numba as nb +import numpy as np + +from .. import anomalous_dimensions as ad +from .. import beta +from . import utils + + +@nb.njit(cache=True) +def eko_iterate(gamma_singlet, a1, a0, nf, order, ev_op_iterations): + """ + Singlet NLO or NNLO iterated (exact) EKO + + Parameters + ---------- + gamma_singlet : numpy.ndarray + singlet anomalous dimensions matrices + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + order : int + perturbative order + ev_op_iterations : int + number of evolution steps + + Returns + ------- + e_s^{order} : numpy.ndarray + singlet NLO or NNLO iterated (exact) EKO + """ + a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) + beta0 = beta.beta_qcd((2, 0), nf) + beta1 = beta.beta_qcd((3, 0), nf) + if order[0] >= 3: + beta2 = beta.beta_qcd((4, 0), nf) + e = np.identity(2, np.complex_) + al = a_steps[0] + for ah in a_steps[1:]: + a_half = (ah + al) / 2.0 + delta_a = ah - al + if order[0] == 2: + ln = ( + (gamma_singlet[0] * a_half + gamma_singlet[1] * a_half**2) + / (beta0 * a_half**2 + beta1 * a_half**3) + * delta_a + ) + elif order[0] == 3: + ln = ( + ( + gamma_singlet[0] * a_half + + gamma_singlet[1] * a_half**2 + + gamma_singlet[2] * a_half**3 + ) + / (beta0 * a_half**2 + beta1 * a_half**3 + beta2 * a_half**4) + * delta_a + ) + ek = np.ascontiguousarray(ad.exp_singlet(ln)[0]) + e = ek @ e + al = ah + return e From af480eec841d24d854b640b265d53ec40e9b164a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 5 Jul 2022 16:22:41 +0200 Subject: [PATCH 005/312] Implement gamma_singlet at O(a^2) --- src/eko/anomalous_dimensions/aem1.py | 13 +++--- src/eko/anomalous_dimensions/aem2.py | 60 +++++++++++++++++++++++++ src/eko/anomalous_dimensions/as1aem1.py | 18 ++++---- tests/eko/test_ad_aem1.py | 5 ++- 4 files changed, 79 insertions(+), 17 deletions(-) diff --git a/src/eko/anomalous_dimensions/aem1.py b/src/eko/anomalous_dimensions/aem1.py index 50540d885..f34f2db8f 100644 --- a/src/eko/anomalous_dimensions/aem1.py +++ b/src/eko/anomalous_dimensions/aem1.py @@ -79,7 +79,7 @@ def gamma_phph(nf): @nb.njit(cache=True) -def gamma_ns(N, s1): +def gamma_ns(N, sx): """ Computes the leading-order non-singlet QED anomalous dimension. @@ -97,11 +97,12 @@ def gamma_ns(N, s1): gamma_ns : complex Leading-order non-singlet QED anomalous dimension :math:`\\gamma_{ns}^{(0)}(N)` """ + s1 = sx[0] return as1.gamma_ns(N, s1) / constants.CF @nb.njit(cache=True) -def gamma_singlet(N, s1, nf): +def gamma_singlet(N, nf, sx): nu = constants.uplike_flavors(nf) nd = nf - nu vu = nu / nf @@ -116,14 +117,14 @@ def gamma_singlet(N, s1, nf): [ 0, e2avg * gamma_qph(N, nf), - e2avg * gamma_ns(N, s1), - vu * e2m * gamma_ns(N, s1), + e2avg * gamma_ns(N, sx), + vu * e2m * gamma_ns(N, sx), ], [ 0, vd * e2m * gamma_qph(N, nf), - vd * e2m * gamma_ns(N, s1), - e2delta * gamma_ns(N, s1), + vd * e2m * gamma_ns(N, sx), + e2delta * gamma_ns(N, sx), ], ] ) diff --git a/src/eko/anomalous_dimensions/aem2.py b/src/eko/anomalous_dimensions/aem2.py index b58175132..fb7da4893 100644 --- a/src/eko/anomalous_dimensions/aem2.py +++ b/src/eko/anomalous_dimensions/aem2.py @@ -4,6 +4,7 @@ """ import numba as nb +import numpy as np from .. import constants from . import as1aem1 @@ -324,3 +325,62 @@ def gamma_ps(N, nf): / ((-1 + N) * N**3 * (1 + N) ** 3 * (2 + N) ** 2) ) return 2 * nf * constants.CA * result + + +@nb.njit(cache=True) +def gamma_singlet(N, nf, sx): + nu = constants.uplike_flavors(nf) + nd = nf - nu + vu = nu / nf + vd = nd / nf + e2avg = (nu * constants.eu2 + nd * constants.ed2) / nf + e2m = constants.eu2 - constants.ed2 + e2delta = (nd * constants.eu2 + nu * constants.ed2) / nf + gamma_S_01 = np.array( + [ + [0, 0, 0, 0], + [ + 0, + gamma_phph(nf), + vu * constants.eu2 * gamma_phu(N, nf, sx) + + vd * constants.ed2 * gamma_phd(N, nf, sx), + vu + * ( + constants.eu2 * gamma_phu(N, nf, sx) + - constants.ed2 * gamma_phd(N, nf, sx) + ), + ], + [ + 0, + vu * constants.eu2 * gamma_uph(N, nf, sx) + + vd * constants.ed2 * gamma_dph(N, nf, sx), + vu * constants.eu2 * gamma_nspu(N, nf, sx) + + vd * constants.ed2 * gamma_nspd(N, nf, sx) + + e2avg**2 * gamma_ps(N, nf), + vu + * ( + constants.eu2 * gamma_nspu(N, nf, sx) + - constants.ed2 * gamma_nspd(N, nf, sx) + + e2m * e2avg * gamma_ps(N, nf) + ), + ], + [ + 0, + vd + * ( + constants.eu2 * gamma_uph(N, nf, sx) + - constants.ed2 * gamma_dph(N, nf, sx) + ), + vd + * ( + constants.eu2 * gamma_nspu(N, nf, sx) + - constants.ed2 * gamma_nspd(N, nf, sx) + + e2m * e2avg * gamma_ps(N, nf) + ), + vd * constants.eu2 * gamma_nspu(N, nf, sx) + + vu * constants.ed2 * gamma_nspd(N, nf, sx) + + vu * vd * e2m**2 * gamma_ps(N, nf), + ], + ] + ) + return gamma_S_01 diff --git a/src/eko/anomalous_dimensions/as1aem1.py b/src/eko/anomalous_dimensions/as1aem1.py index 9ae141d44..fda1228b2 100644 --- a/src/eko/anomalous_dimensions/as1aem1.py +++ b/src/eko/anomalous_dimensions/as1aem1.py @@ -359,7 +359,7 @@ def gamma_nsm(N, sx): @nb.njit(cache=True) -def gamma_singlet(N, s1, nf): +def gamma_singlet(N, nf, sx): nu = constants.uplike_flavors(nf) nd = nf - nu vu = nu / nf @@ -372,8 +372,8 @@ def gamma_singlet(N, s1, nf): [ nf * e2avg * gamma_gg(), nf * e2avg * gamma_gph(N), - nf * e2avg * gamma_gq(N, s1), - vu * e2m * gamma_gq(N, s1), + nf * e2avg * gamma_gq(N, sx), + vu * e2m * gamma_gq(N, sx), ], [ nf * e2avg * gamma_phg(N), @@ -382,16 +382,16 @@ def gamma_singlet(N, s1, nf): vu * e2m * gamma_phq(N), ], [ - e2avg * gamma_qg(N, nf, s1), + e2avg * gamma_qg(N, nf, sx), e2avg * gamma_qph(N, nf), - e2avg * gamma_nsp(N, s1), - vu * e2m * gamma_nsp(N, s1), + e2avg * gamma_nsp(N, sx), + vu * e2m * gamma_nsp(N, sx), ], [ - vd * e2m * gamma_gq(N, s1), + vd * e2m * gamma_gq(N, sx), vd * e2m * gamma_qph(N, nf), - vd * e2m * gamma_nsp(N, s1), - e2delta * gamma_nsp(N, s1), + vd * e2m * gamma_nsp(N, sx), + e2delta * gamma_nsp(N, sx), ], ] ) diff --git a/tests/eko/test_ad_aem1.py b/tests/eko/test_ad_aem1.py index aef86c822..708f38c22 100644 --- a/tests/eko/test_ad_aem1.py +++ b/tests/eko/test_ad_aem1.py @@ -2,6 +2,7 @@ # Test LO splitting functions import numpy as np +import eko.harmonics as h from eko import anomalous_dimensions as ad from eko import constants @@ -9,14 +10,14 @@ def test_number_conservation(): # number N = complex(1.0, 0.0) - s1 = ad.harmonics.S1(N) + s1 = h.sx(N, 1) np.testing.assert_almost_equal(ad.aem1.gamma_ns(N, s1), 0) def test_quark_momentum_conservation(): # quark momentum N = complex(2.0, 0.0) - s1 = ad.harmonics.S1(N) + s1 = h.sx(N, 1) np.testing.assert_almost_equal( ad.aem1.gamma_ns(N, s1) + ad.aem1.gamma_phq(N), 0, From 67edd2e68c0414a7c1e5503576bfcb5fb995fd00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 5 Jul 2022 16:28:54 +0200 Subject: [PATCH 006/312] Fix passing of arguments --- src/eko/anomalous_dimensions/aem2.py | 3 +-- src/eko/anomalous_dimensions/as1aem1.py | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/eko/anomalous_dimensions/aem2.py b/src/eko/anomalous_dimensions/aem2.py index fb7da4893..460d2a9de 100644 --- a/src/eko/anomalous_dimensions/aem2.py +++ b/src/eko/anomalous_dimensions/aem2.py @@ -335,13 +335,12 @@ def gamma_singlet(N, nf, sx): vd = nd / nf e2avg = (nu * constants.eu2 + nd * constants.ed2) / nf e2m = constants.eu2 - constants.ed2 - e2delta = (nd * constants.eu2 + nu * constants.ed2) / nf gamma_S_01 = np.array( [ [0, 0, 0, 0], [ 0, - gamma_phph(nf), + gamma_phph(N, nf), vu * constants.eu2 * gamma_phu(N, nf, sx) + vd * constants.ed2 * gamma_phd(N, nf, sx), vu diff --git a/src/eko/anomalous_dimensions/as1aem1.py b/src/eko/anomalous_dimensions/as1aem1.py index fda1228b2..8e8eafac6 100644 --- a/src/eko/anomalous_dimensions/as1aem1.py +++ b/src/eko/anomalous_dimensions/as1aem1.py @@ -378,8 +378,8 @@ def gamma_singlet(N, nf, sx): [ nf * e2avg * gamma_phg(N), gamma_phph(nf), - e2avg * gamma_phq(N), - vu * e2m * gamma_phq(N), + e2avg * gamma_phq(N, sx), + vu * e2m * gamma_phq(N, sx), ], [ e2avg * gamma_qg(N, nf, sx), @@ -389,7 +389,7 @@ def gamma_singlet(N, nf, sx): ], [ vd * e2m * gamma_gq(N, sx), - vd * e2m * gamma_qph(N, nf), + vd * e2m * gamma_qph(N, nf, sx), vd * e2m * gamma_nsp(N, sx), e2delta * gamma_nsp(N, sx), ], From 22ce44d13b70ae5a03cb424fab916d564338de17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 5 Jul 2022 16:34:27 +0200 Subject: [PATCH 007/312] Implement 2x2 sector at O(a) --- src/eko/anomalous_dimensions/aem1.py | 16 ++++++++++++++++ src/eko/anomalous_dimensions/aem2.py | 4 ++-- src/eko/anomalous_dimensions/as1aem1.py | 6 +++--- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/eko/anomalous_dimensions/aem1.py b/src/eko/anomalous_dimensions/aem1.py index f34f2db8f..19af2421a 100644 --- a/src/eko/anomalous_dimensions/aem1.py +++ b/src/eko/anomalous_dimensions/aem1.py @@ -129,3 +129,19 @@ def gamma_singlet(N, nf, sx): ] ) return gamma_S_01 + + +@nb.njit(cache=True) +def gamma_valence(N, nf, sx): + nu = constants.uplike_flavors(nf) + nd = nf - nu + vu = nu / nf + vd = nd / nf + e2avg = (nu * constants.eu2 + nd * constants.ed2) / nf + e2m = constants.eu2 - constants.ed2 + e2delta = (nd * constants.eu2 + nu * constants.ed2) / nf + gamma_V_01 = np.array( + [e2avg * gamma_ns(N, sx), vu * e2m * gamma_ns(N, sx)], + [vd * e2m * gamma_ns(N, sx), e2delta * gamma_ns(N, sx)], + ) + return gamma_V_01 diff --git a/src/eko/anomalous_dimensions/aem2.py b/src/eko/anomalous_dimensions/aem2.py index 460d2a9de..508be0d42 100644 --- a/src/eko/anomalous_dimensions/aem2.py +++ b/src/eko/anomalous_dimensions/aem2.py @@ -335,7 +335,7 @@ def gamma_singlet(N, nf, sx): vd = nd / nf e2avg = (nu * constants.eu2 + nd * constants.ed2) / nf e2m = constants.eu2 - constants.ed2 - gamma_S_01 = np.array( + gamma_S_02 = np.array( [ [0, 0, 0, 0], [ @@ -382,4 +382,4 @@ def gamma_singlet(N, nf, sx): ], ] ) - return gamma_S_01 + return gamma_S_02 diff --git a/src/eko/anomalous_dimensions/as1aem1.py b/src/eko/anomalous_dimensions/as1aem1.py index 8e8eafac6..72e55a2a4 100644 --- a/src/eko/anomalous_dimensions/as1aem1.py +++ b/src/eko/anomalous_dimensions/as1aem1.py @@ -367,7 +367,7 @@ def gamma_singlet(N, nf, sx): e2avg = (nu * constants.eu2 + nd * constants.ed2) / nf e2m = constants.eu2 - constants.ed2 e2delta = (nd * constants.eu2 + nu * constants.ed2) / nf - gamma_S_01 = np.array( + gamma_S_11 = np.array( [ [ nf * e2avg * gamma_gg(), @@ -383,7 +383,7 @@ def gamma_singlet(N, nf, sx): ], [ e2avg * gamma_qg(N, nf, sx), - e2avg * gamma_qph(N, nf), + e2avg * gamma_qph(N, nf, sx), e2avg * gamma_nsp(N, sx), vu * e2m * gamma_nsp(N, sx), ], @@ -395,4 +395,4 @@ def gamma_singlet(N, nf, sx): ], ] ) - return gamma_S_01 + return gamma_S_11 From ef78105bbf902c16019b68b3643fcf5afff901c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 5 Jul 2022 16:44:11 +0200 Subject: [PATCH 008/312] Implement 2x2 sector at O(as a) and O(a^2) --- src/eko/anomalous_dimensions/aem1.py | 6 +++-- src/eko/anomalous_dimensions/aem2.py | 34 +++++++++++++++++++++++++ src/eko/anomalous_dimensions/as1aem1.py | 18 +++++++++++++ 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/eko/anomalous_dimensions/aem1.py b/src/eko/anomalous_dimensions/aem1.py index 19af2421a..e452c7264 100644 --- a/src/eko/anomalous_dimensions/aem1.py +++ b/src/eko/anomalous_dimensions/aem1.py @@ -141,7 +141,9 @@ def gamma_valence(N, nf, sx): e2m = constants.eu2 - constants.ed2 e2delta = (nd * constants.eu2 + nu * constants.ed2) / nf gamma_V_01 = np.array( - [e2avg * gamma_ns(N, sx), vu * e2m * gamma_ns(N, sx)], - [vd * e2m * gamma_ns(N, sx), e2delta * gamma_ns(N, sx)], + [ + [e2avg * gamma_ns(N, sx), vu * e2m * gamma_ns(N, sx)], + [vd * e2m * gamma_ns(N, sx), e2delta * gamma_ns(N, sx)], + ] ) return gamma_V_01 diff --git a/src/eko/anomalous_dimensions/aem2.py b/src/eko/anomalous_dimensions/aem2.py index 508be0d42..6e9006901 100644 --- a/src/eko/anomalous_dimensions/aem2.py +++ b/src/eko/anomalous_dimensions/aem2.py @@ -383,3 +383,37 @@ def gamma_singlet(N, nf, sx): ] ) return gamma_S_02 + + +@nb.njit(cache=True) +def gamma_valence(N, nf, sx): + nu = constants.uplike_flavors(nf) + nd = nf - nu + vu = nu / nf + vd = nd / nf + e2avg = (nu * constants.eu2 + nd * constants.ed2) / nf + e2m = constants.eu2 - constants.ed2 + e2delta = (nd * constants.eu2 + nu * constants.ed2) / nf + gamma_V_02 = np.array( + [ + [ + vu * constants.eu2 * gamma_nsmu(N, nf, sx) + + vd * constants.ed2 * gamma_nsmd(N, nf, sx), + vu + * ( + constants.eu2 * gamma_nsmu(N, nf, sx) + - constants.ed2 * gamma_nsmd(N, nf, sx) + ), + ], + [ + vd + * ( + constants.eu2 * gamma_nsmu(N, nf, sx) + - constants.ed2 * gamma_nsmd(N, nf, sx) + ), + vu * constants.eu2 * gamma_nsmu(N, nf, sx) + + vd * constants.ed2 * gamma_nsmd(N, nf, sx), + ], + ] + ) + return gamma_V_02 diff --git a/src/eko/anomalous_dimensions/as1aem1.py b/src/eko/anomalous_dimensions/as1aem1.py index 72e55a2a4..e381464bd 100644 --- a/src/eko/anomalous_dimensions/as1aem1.py +++ b/src/eko/anomalous_dimensions/as1aem1.py @@ -396,3 +396,21 @@ def gamma_singlet(N, nf, sx): ] ) return gamma_S_11 + + +@nb.njit(cache=True) +def gamma_valence(N, nf, sx): + nu = constants.uplike_flavors(nf) + nd = nf - nu + vu = nu / nf + vd = nd / nf + e2avg = (nu * constants.eu2 + nd * constants.ed2) / nf + e2m = constants.eu2 - constants.ed2 + e2delta = (nd * constants.eu2 + nu * constants.ed2) / nf + gamma_V_11 = np.array( + [ + [e2avg * gamma_nsm(N, sx), vu * e2m * gamma_nsm(N, sx)], + [vd * e2m * gamma_nsm(N, sx), e2delta * gamma_nsm(N, sx)], + ] + ) + return gamma_V_11 From e5a8ce9d2416f4b77ef2797dd52d976c6baca1cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 5 Jul 2022 16:48:27 +0200 Subject: [PATCH 009/312] Remove unused variables --- src/eko/anomalous_dimensions/aem2.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/eko/anomalous_dimensions/aem2.py b/src/eko/anomalous_dimensions/aem2.py index 6e9006901..6857a94f3 100644 --- a/src/eko/anomalous_dimensions/aem2.py +++ b/src/eko/anomalous_dimensions/aem2.py @@ -391,9 +391,6 @@ def gamma_valence(N, nf, sx): nd = nf - nu vu = nu / nf vd = nd / nf - e2avg = (nu * constants.eu2 + nd * constants.ed2) / nf - e2m = constants.eu2 - constants.ed2 - e2delta = (nd * constants.eu2 + nu * constants.ed2) / nf gamma_V_02 = np.array( [ [ From 5956408ded8c47957fc454ffbd12c7dfc56c94b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 5 Jul 2022 16:50:12 +0200 Subject: [PATCH 010/312] Add np.complex_ in numpy.array --- src/eko/anomalous_dimensions/aem1.py | 6 ++++-- src/eko/anomalous_dimensions/aem2.py | 6 ++++-- src/eko/anomalous_dimensions/as1aem1.py | 6 ++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/eko/anomalous_dimensions/aem1.py b/src/eko/anomalous_dimensions/aem1.py index e452c7264..86a0a283a 100644 --- a/src/eko/anomalous_dimensions/aem1.py +++ b/src/eko/anomalous_dimensions/aem1.py @@ -126,7 +126,8 @@ def gamma_singlet(N, nf, sx): vd * e2m * gamma_ns(N, sx), e2delta * gamma_ns(N, sx), ], - ] + ], + np.complex_, ) return gamma_S_01 @@ -144,6 +145,7 @@ def gamma_valence(N, nf, sx): [ [e2avg * gamma_ns(N, sx), vu * e2m * gamma_ns(N, sx)], [vd * e2m * gamma_ns(N, sx), e2delta * gamma_ns(N, sx)], - ] + ], + np.complex_, ) return gamma_V_01 diff --git a/src/eko/anomalous_dimensions/aem2.py b/src/eko/anomalous_dimensions/aem2.py index 6857a94f3..a5de99d24 100644 --- a/src/eko/anomalous_dimensions/aem2.py +++ b/src/eko/anomalous_dimensions/aem2.py @@ -380,7 +380,8 @@ def gamma_singlet(N, nf, sx): + vu * constants.ed2 * gamma_nspd(N, nf, sx) + vu * vd * e2m**2 * gamma_ps(N, nf), ], - ] + ], + np.complex_, ) return gamma_S_02 @@ -411,6 +412,7 @@ def gamma_valence(N, nf, sx): vu * constants.eu2 * gamma_nsmu(N, nf, sx) + vd * constants.ed2 * gamma_nsmd(N, nf, sx), ], - ] + ], + np.complex_, ) return gamma_V_02 diff --git a/src/eko/anomalous_dimensions/as1aem1.py b/src/eko/anomalous_dimensions/as1aem1.py index e381464bd..f655eff56 100644 --- a/src/eko/anomalous_dimensions/as1aem1.py +++ b/src/eko/anomalous_dimensions/as1aem1.py @@ -393,7 +393,8 @@ def gamma_singlet(N, nf, sx): vd * e2m * gamma_nsp(N, sx), e2delta * gamma_nsp(N, sx), ], - ] + ], + np.complex_, ) return gamma_S_11 @@ -411,6 +412,7 @@ def gamma_valence(N, nf, sx): [ [e2avg * gamma_nsm(N, sx), vu * e2m * gamma_nsm(N, sx)], [vd * e2m * gamma_nsm(N, sx), e2delta * gamma_nsm(N, sx)], - ] + ], + np.complex_, ) return gamma_V_11 From e168d939c6e379c32f569d193e6af37b3c903b4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 6 Jul 2022 09:21:58 +0200 Subject: [PATCH 011/312] Collect variables --- src/eko/anomalous_dimensions/aem1.py | 16 ++++++++------ src/eko/anomalous_dimensions/as1aem1.py | 29 ++++++++++++++----------- src/eko/kernels/QEDsinglet.py | 2 +- 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/src/eko/anomalous_dimensions/aem1.py b/src/eko/anomalous_dimensions/aem1.py index 86a0a283a..2cd5227a5 100644 --- a/src/eko/anomalous_dimensions/aem1.py +++ b/src/eko/anomalous_dimensions/aem1.py @@ -110,20 +110,22 @@ def gamma_singlet(N, nf, sx): e2avg = (nu * constants.eu2 + nd * constants.ed2) / nf e2m = constants.eu2 - constants.ed2 e2delta = (nd * constants.eu2 + nu * constants.ed2) / nf + vue2m = vu * e2m + vde2m = vd * e2m gamma_S_01 = np.array( [ [0, 0, 0, 0], - [0, gamma_phph(nf), e2avg * gamma_phq(N), vu * e2m * gamma_phq(N)], + [0, gamma_phph(nf), e2avg * gamma_phq(N), vue2m * gamma_phq(N)], [ 0, e2avg * gamma_qph(N, nf), e2avg * gamma_ns(N, sx), - vu * e2m * gamma_ns(N, sx), + vue2m * gamma_ns(N, sx), ], [ 0, - vd * e2m * gamma_qph(N, nf), - vd * e2m * gamma_ns(N, sx), + vde2m * gamma_qph(N, nf), + vde2m * gamma_ns(N, sx), e2delta * gamma_ns(N, sx), ], ], @@ -143,9 +145,9 @@ def gamma_valence(N, nf, sx): e2delta = (nd * constants.eu2 + nu * constants.ed2) / nf gamma_V_01 = np.array( [ - [e2avg * gamma_ns(N, sx), vu * e2m * gamma_ns(N, sx)], - [vd * e2m * gamma_ns(N, sx), e2delta * gamma_ns(N, sx)], + [e2avg, vu * e2m], + [vd * e2m, e2delta], ], np.complex_, ) - return gamma_V_01 + return gamma_V_01 * gamma_ns(N, sx) diff --git a/src/eko/anomalous_dimensions/as1aem1.py b/src/eko/anomalous_dimensions/as1aem1.py index f655eff56..208e5592c 100644 --- a/src/eko/anomalous_dimensions/as1aem1.py +++ b/src/eko/anomalous_dimensions/as1aem1.py @@ -365,32 +365,35 @@ def gamma_singlet(N, nf, sx): vu = nu / nf vd = nd / nf e2avg = (nu * constants.eu2 + nd * constants.ed2) / nf + nfe2avg = nf * e2avg e2m = constants.eu2 - constants.ed2 e2delta = (nd * constants.eu2 + nu * constants.ed2) / nf + vue2m = vu * e2m + vde2m = vd * e2m gamma_S_11 = np.array( [ [ - nf * e2avg * gamma_gg(), - nf * e2avg * gamma_gph(N), - nf * e2avg * gamma_gq(N, sx), - vu * e2m * gamma_gq(N, sx), + nfe2avg * gamma_gg(), + nfe2avg * gamma_gph(N), + nfe2avg * gamma_gq(N, sx), + vue2m * gamma_gq(N, sx), ], [ - nf * e2avg * gamma_phg(N), + nfe2avg * gamma_phg(N), gamma_phph(nf), e2avg * gamma_phq(N, sx), - vu * e2m * gamma_phq(N, sx), + vue2m * gamma_phq(N, sx), ], [ e2avg * gamma_qg(N, nf, sx), e2avg * gamma_qph(N, nf, sx), e2avg * gamma_nsp(N, sx), - vu * e2m * gamma_nsp(N, sx), + vue2m * gamma_nsp(N, sx), ], [ - vd * e2m * gamma_gq(N, sx), - vd * e2m * gamma_qph(N, nf, sx), - vd * e2m * gamma_nsp(N, sx), + vde2m * gamma_gq(N, sx), + vde2m * gamma_qph(N, nf, sx), + vde2m * gamma_nsp(N, sx), e2delta * gamma_nsp(N, sx), ], ], @@ -410,9 +413,9 @@ def gamma_valence(N, nf, sx): e2delta = (nd * constants.eu2 + nu * constants.ed2) / nf gamma_V_11 = np.array( [ - [e2avg * gamma_nsm(N, sx), vu * e2m * gamma_nsm(N, sx)], - [vd * e2m * gamma_nsm(N, sx), e2delta * gamma_nsm(N, sx)], + [e2avg, vu * e2m], + [vd * e2m, e2delta], ], np.complex_, ) - return gamma_V_11 + return gamma_V_11 * gamma_nsm(N, sx) diff --git a/src/eko/kernels/QEDsinglet.py b/src/eko/kernels/QEDsinglet.py index e8df7d143..7217bcdca 100644 --- a/src/eko/kernels/QEDsinglet.py +++ b/src/eko/kernels/QEDsinglet.py @@ -37,7 +37,7 @@ def eko_iterate(gamma_singlet, a1, a0, nf, order, ev_op_iterations): beta1 = beta.beta_qcd((3, 0), nf) if order[0] >= 3: beta2 = beta.beta_qcd((4, 0), nf) - e = np.identity(2, np.complex_) + e = np.identity(4, np.complex_) al = a_steps[0] for ah in a_steps[1:]: a_half = (ah + al) / 2.0 From d47dff32b88190a6b9b8898b17acb79df4376f5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 6 Jul 2022 10:26:26 +0200 Subject: [PATCH 012/312] Implement 4x4 sector for O(as1) --- src/eko/anomalous_dimensions/as1.py | 45 +++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/eko/anomalous_dimensions/as1.py b/src/eko/anomalous_dimensions/as1.py index a178a3f08..39564c73e 100644 --- a/src/eko/anomalous_dimensions/as1.py +++ b/src/eko/anomalous_dimensions/as1.py @@ -140,3 +140,48 @@ def gamma_singlet(N, s1, nf): [[gamma_qq, gamma_qg(N, nf)], [gamma_gq(N), gamma_gg(N, s1, nf)]], np.complex_ ) return gamma_S_0 + + +@nb.njit(cache=True) +def gamma_QEDsinglet(N, s1, nf): + r""" + Computes the leading-order singlet anomalous dimension matrix + + .. math:: + \gamma_S^{(0)} = \left(\begin{array}{cc} + \gamma_{qq}^{(0)} & \gamma_{qg}^{(0)}\\ + \gamma_{gq}^{(0)} & \gamma_{gg}^{(0)} + \end{array}\right) + + Parameters + ---------- + N : complex + Mellin moment + s1 : complex + harmonic sum :math:`S_{1}` + nf : int + Number of active flavors + + Returns + ------- + gamma_S_0 : numpy.ndarray + Leading-order singlet anomalous dimension matrix :math:`\gamma_{S}^{(0)}(N)` + + See Also + -------- + gamma_ns : :math:`\gamma_{qq}^{(0)}` + gamma_qg : :math:`\gamma_{qg}^{(0)}` + gamma_gq : :math:`\gamma_{gq}^{(0)}` + gamma_gg : :math:`\gamma_{gg}^{(0)}` + """ + gamma_qq = gamma_ns(N, s1) + gamma_S_0 = np.array( + [ + [gamma_gg(N, s1, nf), 0, gamma_gq(N), 0], + [0, 0, 0, 0], + [gamma_qg(N, nf), 0, gamma_qq, 0], + [0, 0, 0, gamma_qq], + ], + np.complex_, + ) + return gamma_S_0 From b1eb68888e60f233b0c8c13d3fcc5efe63a44344 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 6 Jul 2022 10:29:17 +0200 Subject: [PATCH 013/312] Implement 4x4 sector for O(as2) --- src/eko/anomalous_dimensions/as1.py | 4 +-- src/eko/anomalous_dimensions/as2.py | 45 +++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/eko/anomalous_dimensions/as1.py b/src/eko/anomalous_dimensions/as1.py index 39564c73e..b3ed9bc99 100644 --- a/src/eko/anomalous_dimensions/as1.py +++ b/src/eko/anomalous_dimensions/as1.py @@ -175,7 +175,7 @@ def gamma_QEDsinglet(N, s1, nf): gamma_gg : :math:`\gamma_{gg}^{(0)}` """ gamma_qq = gamma_ns(N, s1) - gamma_S_0 = np.array( + gamma_S = np.array( [ [gamma_gg(N, s1, nf), 0, gamma_gq(N), 0], [0, 0, 0, 0], @@ -184,4 +184,4 @@ def gamma_QEDsinglet(N, s1, nf): ], np.complex_, ) - return gamma_S_0 + return gamma_S diff --git a/src/eko/anomalous_dimensions/as2.py b/src/eko/anomalous_dimensions/as2.py index 4dd6088c5..37c2475f9 100644 --- a/src/eko/anomalous_dimensions/as2.py +++ b/src/eko/anomalous_dimensions/as2.py @@ -275,3 +275,48 @@ def gamma_singlet(n, nf, sx): np.complex_, ) return gamma_S_0 + + +@nb.njit(cache=True) +def gamma_QEDsinglet(N, sx, nf): + r""" + Computes the leading-order singlet anomalous dimension matrix + + .. math:: + \gamma_S^{(0)} = \left(\begin{array}{cc} + \gamma_{qq}^{(0)} & \gamma_{qg}^{(0)}\\ + \gamma_{gq}^{(0)} & \gamma_{gg}^{(0)} + \end{array}\right) + + Parameters + ---------- + N : complex + Mellin moment + s1 : complex + harmonic sum :math:`S_{1}` + nf : int + Number of active flavors + + Returns + ------- + gamma_S_0 : numpy.ndarray + Leading-order singlet anomalous dimension matrix :math:`\gamma_{S}^{(0)}(N)` + + See Also + -------- + gamma_ns : :math:`\gamma_{qq}^{(0)}` + gamma_qg : :math:`\gamma_{qg}^{(0)}` + gamma_gq : :math:`\gamma_{gq}^{(0)}` + gamma_gg : :math:`\gamma_{gg}^{(0)}` + """ + gamma_qq = gamma_nsp(N, nf, sx) + gamma_ps(N, nf) + gamma_S = np.array( + [ + [gamma_gg(N, sx, nf), 0, gamma_gq(N), 0], + [0, 0, 0, 0], + [gamma_qg(N, nf), 0, gamma_qq, 0], + [0, 0, 0, gamma_qq], + ], + np.complex_, + ) + return gamma_S From bb2847b761ccfb92dde1e36f9bd8b9c49ee699d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 6 Jul 2022 10:33:56 +0200 Subject: [PATCH 014/312] Implement 4x4 sector for O(as3) --- src/eko/anomalous_dimensions/as2.py | 6 ++-- src/eko/anomalous_dimensions/as3.py | 45 +++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/src/eko/anomalous_dimensions/as2.py b/src/eko/anomalous_dimensions/as2.py index 37c2475f9..1b162a723 100644 --- a/src/eko/anomalous_dimensions/as2.py +++ b/src/eko/anomalous_dimensions/as2.py @@ -312,10 +312,10 @@ def gamma_QEDsinglet(N, sx, nf): gamma_qq = gamma_nsp(N, nf, sx) + gamma_ps(N, nf) gamma_S = np.array( [ - [gamma_gg(N, sx, nf), 0, gamma_gq(N), 0], + [gamma_gg(N, nf, sx), 0, gamma_gq(N, nf, sx), 0], [0, 0, 0, 0], - [gamma_qg(N, nf), 0, gamma_qq, 0], - [0, 0, 0, gamma_qq], + [gamma_qg(N, nf, sx), 0, gamma_qq, 0], + [0, 0, 0, gamma_nsp(N, nf, sx)], ], np.complex_, ) diff --git a/src/eko/anomalous_dimensions/as3.py b/src/eko/anomalous_dimensions/as3.py index 49fe1cc0f..2cc4d58b9 100644 --- a/src/eko/anomalous_dimensions/as3.py +++ b/src/eko/anomalous_dimensions/as3.py @@ -586,3 +586,48 @@ def gamma_singlet(N, nf, sx): np.complex_, ) return gamma_S_0 + + +@nb.njit(cache=True) +def gamma_QEDsinglet(N, sx, nf): + r""" + Computes the leading-order singlet anomalous dimension matrix + + .. math:: + \gamma_S^{(0)} = \left(\begin{array}{cc} + \gamma_{qq}^{(0)} & \gamma_{qg}^{(0)}\\ + \gamma_{gq}^{(0)} & \gamma_{gg}^{(0)} + \end{array}\right) + + Parameters + ---------- + N : complex + Mellin moment + s1 : complex + harmonic sum :math:`S_{1}` + nf : int + Number of active flavors + + Returns + ------- + gamma_S_0 : numpy.ndarray + Leading-order singlet anomalous dimension matrix :math:`\gamma_{S}^{(0)}(N)` + + See Also + -------- + gamma_ns : :math:`\gamma_{qq}^{(0)}` + gamma_qg : :math:`\gamma_{qg}^{(0)}` + gamma_gq : :math:`\gamma_{gq}^{(0)}` + gamma_gg : :math:`\gamma_{gg}^{(0)}` + """ + gamma_qq = gamma_nsp(N, nf, sx) + gamma_ps(N, nf, sx) + gamma_S = np.array( + [ + [gamma_gg(N, nf, sx), 0, gamma_gq(N, nf, sx), 0], + [0, 0, 0, 0], + [gamma_qg(N, nf, sx), 0, gamma_qq, 0], + [0, 0, 0, gamma_nsp(N, nf, sx)], + ], + np.complex_, + ) + return gamma_S From 854ec6d95fda6456296c882b13f3276e549b2f0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 6 Jul 2022 10:57:06 +0200 Subject: [PATCH 015/312] Implement 2x2 sectors for QCD only --- src/eko/anomalous_dimensions/as1.py | 12 ++++++++++++ src/eko/anomalous_dimensions/as2.py | 12 ++++++++++++ src/eko/anomalous_dimensions/as3.py | 12 ++++++++++++ 3 files changed, 36 insertions(+) diff --git a/src/eko/anomalous_dimensions/as1.py b/src/eko/anomalous_dimensions/as1.py index b3ed9bc99..bd848a201 100644 --- a/src/eko/anomalous_dimensions/as1.py +++ b/src/eko/anomalous_dimensions/as1.py @@ -185,3 +185,15 @@ def gamma_QEDsinglet(N, s1, nf): np.complex_, ) return gamma_S + + +@nb.njit(cache=True) +def gamma_QEDvalence(N, s1): + gamma_V = np.array( + [ + [1, 0], + [0, 1], + ], + np.complex_, + ) + return gamma_V * gamma_ns(N, s1) diff --git a/src/eko/anomalous_dimensions/as2.py b/src/eko/anomalous_dimensions/as2.py index 1b162a723..0fd629418 100644 --- a/src/eko/anomalous_dimensions/as2.py +++ b/src/eko/anomalous_dimensions/as2.py @@ -320,3 +320,15 @@ def gamma_QEDsinglet(N, sx, nf): np.complex_, ) return gamma_S + + +@nb.njit(cache=True) +def gamma_QEDvalence(N, nf, sx): + gamma_V = np.array( + [ + [1, 0], + [0, 1], + ], + np.complex_, + ) + return gamma_V * gamma_nsm(N, nf, sx) diff --git a/src/eko/anomalous_dimensions/as3.py b/src/eko/anomalous_dimensions/as3.py index 2cc4d58b9..a2dbf5ee6 100644 --- a/src/eko/anomalous_dimensions/as3.py +++ b/src/eko/anomalous_dimensions/as3.py @@ -631,3 +631,15 @@ def gamma_QEDsinglet(N, sx, nf): np.complex_, ) return gamma_S + + +@nb.njit(cache=True) +def gamma_QEDvalence(N, nf, sx): + gamma_V = np.array( + [ + [gamma_nsv(N, nf, sx), 0], + [0, gamma_nsm(N, nf, sx)], + ], + np.complex_, + ) + return gamma_V From 85b8b4fd7a3ecd396b4eab6c356fa89419073a36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 6 Jul 2022 11:02:22 +0200 Subject: [PATCH 016/312] Fix order in arguments --- src/eko/anomalous_dimensions/as2.py | 2 +- src/eko/anomalous_dimensions/as3.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/eko/anomalous_dimensions/as2.py b/src/eko/anomalous_dimensions/as2.py index 0fd629418..227e7e8f4 100644 --- a/src/eko/anomalous_dimensions/as2.py +++ b/src/eko/anomalous_dimensions/as2.py @@ -278,7 +278,7 @@ def gamma_singlet(n, nf, sx): @nb.njit(cache=True) -def gamma_QEDsinglet(N, sx, nf): +def gamma_QEDsinglet(N, nf, sx): r""" Computes the leading-order singlet anomalous dimension matrix diff --git a/src/eko/anomalous_dimensions/as3.py b/src/eko/anomalous_dimensions/as3.py index a2dbf5ee6..43b3e6cff 100644 --- a/src/eko/anomalous_dimensions/as3.py +++ b/src/eko/anomalous_dimensions/as3.py @@ -589,7 +589,7 @@ def gamma_singlet(N, nf, sx): @nb.njit(cache=True) -def gamma_QEDsinglet(N, sx, nf): +def gamma_QEDsinglet(N, nf, sx): r""" Computes the leading-order singlet anomalous dimension matrix From b0c8a510c0c006d0eb889cc85a2aed2813a0d79f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 6 Jul 2022 12:50:51 +0200 Subject: [PATCH 017/312] rename nfe2avg -> e2_tot --- src/eko/anomalous_dimensions/as1aem1.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/eko/anomalous_dimensions/as1aem1.py b/src/eko/anomalous_dimensions/as1aem1.py index 208e5592c..24b15cc0b 100644 --- a/src/eko/anomalous_dimensions/as1aem1.py +++ b/src/eko/anomalous_dimensions/as1aem1.py @@ -365,7 +365,7 @@ def gamma_singlet(N, nf, sx): vu = nu / nf vd = nd / nf e2avg = (nu * constants.eu2 + nd * constants.ed2) / nf - nfe2avg = nf * e2avg + e2_tot = nf * e2avg e2m = constants.eu2 - constants.ed2 e2delta = (nd * constants.eu2 + nu * constants.ed2) / nf vue2m = vu * e2m @@ -373,13 +373,13 @@ def gamma_singlet(N, nf, sx): gamma_S_11 = np.array( [ [ - nfe2avg * gamma_gg(), - nfe2avg * gamma_gph(N), - nfe2avg * gamma_gq(N, sx), + e2_tot * gamma_gg(), + e2_tot * gamma_gph(N), + e2_tot * gamma_gq(N, sx), vue2m * gamma_gq(N, sx), ], [ - nfe2avg * gamma_phg(N), + e2_tot * gamma_phg(N), gamma_phph(nf), e2avg * gamma_phq(N, sx), vue2m * gamma_phq(N, sx), From c170f8493f764b1c1afbc67a4986211e5c4abe0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 6 Jul 2022 14:09:41 +0200 Subject: [PATCH 018/312] Implement eko_iterate for 4x4 sector --- src/eko/kernels/QEDsinglet.py | 43 ++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/src/eko/kernels/QEDsinglet.py b/src/eko/kernels/QEDsinglet.py index 7217bcdca..84367ab86 100644 --- a/src/eko/kernels/QEDsinglet.py +++ b/src/eko/kernels/QEDsinglet.py @@ -8,7 +8,7 @@ @nb.njit(cache=True) -def eko_iterate(gamma_singlet, a1, a0, nf, order, ev_op_iterations): +def eko_iterate(gamma_singlet, a1, a0, aem, nf, order, ev_op_iterations): """ Singlet NLO or NNLO iterated (exact) EKO @@ -33,31 +33,32 @@ def eko_iterate(gamma_singlet, a1, a0, nf, order, ev_op_iterations): singlet NLO or NNLO iterated (exact) EKO """ a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) - beta0 = beta.beta_qcd((2, 0), nf) - beta1 = beta.beta_qcd((3, 0), nf) - if order[0] >= 3: - beta2 = beta.beta_qcd((4, 0), nf) e = np.identity(4, np.complex_) al = a_steps[0] + betaQCD = np.array( + [ + [ + beta.beta_qcd((2, 0), nf), + beta.beta_qcd((3, 0), nf), + beta.beta_qcd((4, 0), nf), + beta.beta_qcd((5, 0), nf), + ], + [beta.beta_qcd((2, 1), nf), 0, 0, 0], + [0, 0, 0, 0], + ] + ) for ah in a_steps[1:]: a_half = (ah + al) / 2.0 delta_a = ah - al - if order[0] == 2: - ln = ( - (gamma_singlet[0] * a_half + gamma_singlet[1] * a_half**2) - / (beta0 * a_half**2 + beta1 * a_half**3) - * delta_a - ) - elif order[0] == 3: - ln = ( - ( - gamma_singlet[0] * a_half - + gamma_singlet[1] * a_half**2 - + gamma_singlet[2] * a_half**3 - ) - / (beta0 * a_half**2 + beta1 * a_half**3 + beta2 * a_half**4) - * delta_a - ) + gamma = np.zeros((4, 4), np.complex_) + betatot = 0 + for i in range(0, order[0] + 1): + for j in range(0, order[1] + 1): + betatot += a_half**2 * betaQCD[i, j] * a_half**i * aem**j + if (i, j) == (0, 0): + continue # this is probably useless + gamma += gamma_singlet[i, j] * a_half**i * aem**j + ln = gamma / betatot * delta_a ek = np.ascontiguousarray(ad.exp_singlet(ln)[0]) e = ek @ e al = ah From 5c477081662794cf948a4fefa15309d832c65e6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 6 Jul 2022 14:37:53 +0200 Subject: [PATCH 019/312] Implement eko_iterate for QEDvalence --- src/eko/kernels/QEDsinglet.py | 2 +- src/eko/kernels/QEDvalence.py | 65 +++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 src/eko/kernels/QEDvalence.py diff --git a/src/eko/kernels/QEDsinglet.py b/src/eko/kernels/QEDsinglet.py index 84367ab86..7a5be909c 100644 --- a/src/eko/kernels/QEDsinglet.py +++ b/src/eko/kernels/QEDsinglet.py @@ -59,7 +59,7 @@ def eko_iterate(gamma_singlet, a1, a0, aem, nf, order, ev_op_iterations): continue # this is probably useless gamma += gamma_singlet[i, j] * a_half**i * aem**j ln = gamma / betatot * delta_a - ek = np.ascontiguousarray(ad.exp_singlet(ln)[0]) + ek = np.ascontiguousarray(ad.exp_4x4_sector(ln)[0]) e = ek @ e al = ah return e diff --git a/src/eko/kernels/QEDvalence.py b/src/eko/kernels/QEDvalence.py new file mode 100644 index 000000000..2842a3cf7 --- /dev/null +++ b/src/eko/kernels/QEDvalence.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +import numba as nb +import numpy as np + +from .. import anomalous_dimensions as ad +from .. import beta +from . import utils + + +@nb.njit(cache=True) +def eko_iterate(gamma_valence, a1, a0, aem, nf, order, ev_op_iterations): + """ + Singlet NLO or NNLO iterated (exact) EKO + + Parameters + ---------- + gamma_singlet : numpy.ndarray + singlet anomalous dimensions matrices + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + order : int + perturbative order + ev_op_iterations : int + number of evolution steps + + Returns + ------- + e_s^{order} : numpy.ndarray + singlet NLO or NNLO iterated (exact) EKO + """ + a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) + e = np.identity(2, np.complex_) + al = a_steps[0] + betaQCD = np.array( + [ + [ + beta.beta_qcd((2, 0), nf), + beta.beta_qcd((3, 0), nf), + beta.beta_qcd((4, 0), nf), + beta.beta_qcd((5, 0), nf), + ], + [beta.beta_qcd((2, 1), nf), 0, 0, 0], + [0, 0, 0, 0], + ] + ) + for ah in a_steps[1:]: + a_half = (ah + al) / 2.0 + delta_a = ah - al + gamma = np.zeros((2, 2), np.complex_) + betatot = 0 + for i in range(0, order[0] + 1): + for j in range(0, order[1] + 1): + betatot += a_half**2 * betaQCD[i, j] * a_half**i * aem**j + if (i, j) == (0, 0): + continue # this is probably useless + gamma += gamma_valence[i, j] * a_half**i * aem**j + ln = gamma / betatot * delta_a + ek = np.ascontiguousarray(ad.exp_singlet(ln)[0]) + e = ek @ e + al = ah + return e From d8c187bf08f61df48ad8de33d89db480be584693 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 6 Jul 2022 15:57:28 +0200 Subject: [PATCH 020/312] Remove unnecessary if --- src/eko/kernels/QEDsinglet.py | 2 -- src/eko/kernels/QEDvalence.py | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/eko/kernels/QEDsinglet.py b/src/eko/kernels/QEDsinglet.py index 7a5be909c..d9268dfe6 100644 --- a/src/eko/kernels/QEDsinglet.py +++ b/src/eko/kernels/QEDsinglet.py @@ -55,8 +55,6 @@ def eko_iterate(gamma_singlet, a1, a0, aem, nf, order, ev_op_iterations): for i in range(0, order[0] + 1): for j in range(0, order[1] + 1): betatot += a_half**2 * betaQCD[i, j] * a_half**i * aem**j - if (i, j) == (0, 0): - continue # this is probably useless gamma += gamma_singlet[i, j] * a_half**i * aem**j ln = gamma / betatot * delta_a ek = np.ascontiguousarray(ad.exp_4x4_sector(ln)[0]) diff --git a/src/eko/kernels/QEDvalence.py b/src/eko/kernels/QEDvalence.py index 2842a3cf7..aa27918bb 100644 --- a/src/eko/kernels/QEDvalence.py +++ b/src/eko/kernels/QEDvalence.py @@ -55,8 +55,6 @@ def eko_iterate(gamma_valence, a1, a0, aem, nf, order, ev_op_iterations): for i in range(0, order[0] + 1): for j in range(0, order[1] + 1): betatot += a_half**2 * betaQCD[i, j] * a_half**i * aem**j - if (i, j) == (0, 0): - continue # this is probably useless gamma += gamma_valence[i, j] * a_half**i * aem**j ln = gamma / betatot * delta_a ek = np.ascontiguousarray(ad.exp_singlet(ln)[0]) From d92464459b6b31fbf50cf5c318b1ac2ad5c7aaac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 7 Jul 2022 14:33:59 +0200 Subject: [PATCH 021/312] Implement dispatcher for QED singlet and valence --- src/eko/kernels/QEDsinglet.py | 67 +++++++++++++++++++++++++++++++++-- src/eko/kernels/QEDvalence.py | 67 +++++++++++++++++++++++++++++++++-- 2 files changed, 130 insertions(+), 4 deletions(-) diff --git a/src/eko/kernels/QEDsinglet.py b/src/eko/kernels/QEDsinglet.py index d9268dfe6..6b053c9ac 100644 --- a/src/eko/kernels/QEDsinglet.py +++ b/src/eko/kernels/QEDsinglet.py @@ -5,6 +5,7 @@ from .. import anomalous_dimensions as ad from .. import beta from . import utils +from .singlet import lo_exact @nb.njit(cache=True) @@ -52,8 +53,8 @@ def eko_iterate(gamma_singlet, a1, a0, aem, nf, order, ev_op_iterations): delta_a = ah - al gamma = np.zeros((4, 4), np.complex_) betatot = 0 - for i in range(0, order[0] + 1): - for j in range(0, order[1] + 1): + for i in range(0, order[0]): + for j in range(0, order[1]): betatot += a_half**2 * betaQCD[i, j] * a_half**i * aem**j gamma += gamma_singlet[i, j] * a_half**i * aem**j ln = gamma / betatot * delta_a @@ -61,3 +62,65 @@ def eko_iterate(gamma_singlet, a1, a0, aem, nf, order, ev_op_iterations): e = ek @ e al = ah return e + + +@nb.njit(cache=True) +def dispatcher( # pylint: disable=too-many-return-statements + order, method, gamma_singlet, a1, a0, aem, nf, ev_op_iterations, ev_op_max_order +): + """ + Determine used kernel and call it. + + In LO we always use the exact solution. + + Parameters + ---------- + order : tuple(int,int) + perturbative order + method : str + method + gamma_singlet : numpy.ndarray + singlet anomalous dimensions matrices + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + ev_op_iterations : int + number of evolution steps + ev_op_max_order : tuple(int,int) + perturbative expansion order of U + + Returns + ------- + e_s : numpy.ndarray + singlet EKO + """ + # use always exact in LO + # if order == (1,0): + # return lo_exact(gamma_singlet, a1, a0, nf) + + if method in ["iterate-exact", "iterate-expanded"]: + return eko_iterate(gamma_singlet, a1, a0, aem, nf, order, ev_op_iterations) + + # if method == "perturbative-exact": + # return eko_perturbative( + # gamma_singlet, a1, a0, nf, order, ev_op_iterations, ev_op_max_order, True + # ) + # if method == "perturbative-expanded": + # return eko_perturbative( + # gamma_singlet, a1, a0, nf, order, ev_op_iterations, ev_op_max_order, False + # ) + # if method in ["truncated", "ordered-truncated"]: + # return eko_truncated(gamma_singlet, a1, a0, nf, order, ev_op_iterations) + # # These methods are scattered for nlo and nnlo + # if method == "decompose-exact": + # if order[0] == 2: + # return nlo_decompose_exact(gamma_singlet, a1, a0, nf) + # return nnlo_decompose_exact(gamma_singlet, a1, a0, nf) + # if method == "decompose-expanded": + # if order[0] == 2: + # return nlo_decompose_expanded(gamma_singlet, a1, a0, nf) + # return nnlo_decompose_expanded(gamma_singlet, a1, a0, nf) + raise NotImplementedError("Selected method is not implemented") diff --git a/src/eko/kernels/QEDvalence.py b/src/eko/kernels/QEDvalence.py index aa27918bb..f4b6a1d0c 100644 --- a/src/eko/kernels/QEDvalence.py +++ b/src/eko/kernels/QEDvalence.py @@ -52,12 +52,75 @@ def eko_iterate(gamma_valence, a1, a0, aem, nf, order, ev_op_iterations): delta_a = ah - al gamma = np.zeros((2, 2), np.complex_) betatot = 0 - for i in range(0, order[0] + 1): - for j in range(0, order[1] + 1): + for i in range(0, order[0]): + for j in range(0, order[1]): betatot += a_half**2 * betaQCD[i, j] * a_half**i * aem**j gamma += gamma_valence[i, j] * a_half**i * aem**j ln = gamma / betatot * delta_a ek = np.ascontiguousarray(ad.exp_singlet(ln)[0]) + # maybe the name singlet is confusing at this point: better 2x2_sector e = ek @ e al = ah return e + + +@nb.njit(cache=True) +def dispatcher( # pylint: disable=too-many-return-statements + order, method, gamma_valence, a1, a0, aem, nf, ev_op_iterations, ev_op_max_order +): + """ + Determine used kernel and call it. + + In LO we always use the exact solution. + + Parameters + ---------- + order : tuple(int,int) + perturbative order + method : str + method + gamma_singlet : numpy.ndarray + singlet anomalous dimensions matrices + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + ev_op_iterations : int + number of evolution steps + ev_op_max_order : tuple(int,int) + perturbative expansion order of U + + Returns + ------- + e_s : numpy.ndarray + singlet EKO + """ + # use always exact in LO + # if order == (1,0): + # return lo_exact(gamma_singlet, a1, a0, nf) + + if method in ["iterate-exact", "iterate-expanded"]: + return eko_iterate(gamma_valence, a1, a0, aem, nf, order, ev_op_iterations) + + # if method == "perturbative-exact": + # return eko_perturbative( + # gamma_singlet, a1, a0, nf, order, ev_op_iterations, ev_op_max_order, True + # ) + # if method == "perturbative-expanded": + # return eko_perturbative( + # gamma_singlet, a1, a0, nf, order, ev_op_iterations, ev_op_max_order, False + # ) + # if method in ["truncated", "ordered-truncated"]: + # return eko_truncated(gamma_singlet, a1, a0, nf, order, ev_op_iterations) + # # These methods are scattered for nlo and nnlo + # if method == "decompose-exact": + # if order[0] == 2: + # return nlo_decompose_exact(gamma_singlet, a1, a0, nf) + # return nnlo_decompose_exact(gamma_singlet, a1, a0, nf) + # if method == "decompose-expanded": + # if order[0] == 2: + # return nlo_decompose_expanded(gamma_singlet, a1, a0, nf) + # return nnlo_decompose_expanded(gamma_singlet, a1, a0, nf) + raise NotImplementedError("Selected method is not implemented") From d23289a27e40273d55bed1fe0350f4bcbb0cf479 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 7 Jul 2022 16:17:09 +0200 Subject: [PATCH 022/312] Implement array of gamma_singlet and gamma_valence --- src/eko/anomalous_dimensions/__init__.py | 88 ++++++++++++++++++++++++ src/eko/kernels/QEDsinglet.py | 32 +++++---- src/eko/kernels/QEDvalence.py | 32 +++++---- 3 files changed, 124 insertions(+), 28 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index 9561380f1..473140c43 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -167,6 +167,94 @@ def gamma_singlet(order, n, nf): return gamma_s +@nb.njit(cache=True) +def gamma_4x4sector(order, n, nf): + r""" + Computes the tower of the singlet anomalous dimensions matrices + + Parameters + ---------- + order : tuple(int,int) + perturbative orders + n : complex + Mellin variable + nf : int + Number of active flavors + + Returns + ------- + gamma_singlet : numpy.ndarray + singlet anomalous dimensions matrices + + See Also + -------- + eko.anomalous_dimensions.as1.gamma_singlet : :math:`\gamma_{S}^{(0)}(N)` + eko.anomalous_dimensions.as2.gamma_singlet : :math:`\gamma_{S}^{(1)}(N)` + eko.anomalous_dimensions.as3.gamma_singlet : :math:`\gamma_{S}^{(2)}(N)` + """ + # cache the s-es + sx = harmonics.sx(n, max_weight=order[0] + 1) + gamma_s = np.zeros((order[0] + 1, order[1] + 1, 4, 4), np.complex_) + if order[0] >= 1: + gamma_s[1, 0] = as1.gamma_QEDsinglet(n, sx[0], nf) + if order[1] >= 1: + gamma_s[0, 1] = aem1.gamma_singlet(n, nf, sx[0]) + if order[0] >= 1 and order[1] >= 1: + gamma_s[1, 1] = as1aem1.gamma_singlet(n, nf, sx) + if order[0] >= 2: + gamma_s[2, 0] = as2.gamma_QEDsinglet(n, nf, sx) + if order[1] >= 2: + gamma_s[0, 2] = aem2.gamma_singlet(n, nf, sx) + if order[0] == 3: + sx = np.append(sx, harmonics.S4(n)) + gamma_s[3, 0] = -as3.gamma_QEDsinglet(n, nf, sx) + return gamma_s + + +@nb.njit(cache=True) +def gamma_4x4sector(order, n, nf): + r""" + Computes the tower of the singlet anomalous dimensions matrices + + Parameters + ---------- + order : tuple(int,int) + perturbative orders + n : complex + Mellin variable + nf : int + Number of active flavors + + Returns + ------- + gamma_singlet : numpy.ndarray + singlet anomalous dimensions matrices + + See Also + -------- + eko.anomalous_dimensions.as1.gamma_singlet : :math:`\gamma_{S}^{(0)}(N)` + eko.anomalous_dimensions.as2.gamma_singlet : :math:`\gamma_{S}^{(1)}(N)` + eko.anomalous_dimensions.as3.gamma_singlet : :math:`\gamma_{S}^{(2)}(N)` + """ + # cache the s-es + sx = harmonics.sx(n, max_weight=order[0] + 1) + gamma_s = np.zeros((order[0] + 1, order[1] + 1, 2, 2), np.complex_) + if order[0] >= 1: + gamma_s[1, 0] = as1.gamma_QEDvalence(n, sx[0], nf) + if order[1] >= 1: + gamma_s[0, 1] = aem1.gamma_valence(n, nf, sx[0]) + if order[0] >= 1 and order[1] >= 1: + gamma_s[1, 1] = as1aem1.gamma_valence(n, nf, sx) + if order[0] >= 2: + gamma_s[2, 0] = as2.gamma_QEDvalence(n, nf, sx) + if order[1] >= 2: + gamma_s[0, 2] = aem2.gamma_valence(n, nf, sx) + if order[0] == 3: + sx = np.append(sx, harmonics.S4(n)) + gamma_s[3, 0] = -as3.gamma_QEDvalence(n, nf, sx) + return gamma_s + + @nb.njit(cache=True) def exp_4x4_sector(gamma_S): r""" diff --git a/src/eko/kernels/QEDsinglet.py b/src/eko/kernels/QEDsinglet.py index 6b053c9ac..ae6e66786 100644 --- a/src/eko/kernels/QEDsinglet.py +++ b/src/eko/kernels/QEDsinglet.py @@ -36,25 +36,29 @@ def eko_iterate(gamma_singlet, a1, a0, aem, nf, order, ev_op_iterations): a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) e = np.identity(4, np.complex_) al = a_steps[0] - betaQCD = np.array( - [ - [ - beta.beta_qcd((2, 0), nf), - beta.beta_qcd((3, 0), nf), - beta.beta_qcd((4, 0), nf), - beta.beta_qcd((5, 0), nf), - ], - [beta.beta_qcd((2, 1), nf), 0, 0, 0], - [0, 0, 0, 0], - ] - ) + # betaQCD = np.array( + # [ + # [ + # beta.beta_qcd((2, 0), nf), + # beta.beta_qcd((3, 0), nf), + # beta.beta_qcd((4, 0), nf), + # beta.beta_qcd((5, 0), nf), + # ], + # [beta.beta_qcd((2, 1), nf), 0, 0, 0], + # [0, 0, 0, 0], + # ] + # ) + betaQCD = np.zeros((4, 4), np.complex_) + for i in range(4): + betaQCD[i, 0] = beta.beta_qcd((i + 2, 0), nf) + betaQCD[0, 1] = beta.beta_qcd((2, 1), nf) for ah in a_steps[1:]: a_half = (ah + al) / 2.0 delta_a = ah - al gamma = np.zeros((4, 4), np.complex_) betatot = 0 - for i in range(0, order[0]): - for j in range(0, order[1]): + for i in range(0, order[0] + 1): + for j in range(0, order[1] + 1): betatot += a_half**2 * betaQCD[i, j] * a_half**i * aem**j gamma += gamma_singlet[i, j] * a_half**i * aem**j ln = gamma / betatot * delta_a diff --git a/src/eko/kernels/QEDvalence.py b/src/eko/kernels/QEDvalence.py index f4b6a1d0c..b08f0b2ef 100644 --- a/src/eko/kernels/QEDvalence.py +++ b/src/eko/kernels/QEDvalence.py @@ -35,25 +35,29 @@ def eko_iterate(gamma_valence, a1, a0, aem, nf, order, ev_op_iterations): a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) e = np.identity(2, np.complex_) al = a_steps[0] - betaQCD = np.array( - [ - [ - beta.beta_qcd((2, 0), nf), - beta.beta_qcd((3, 0), nf), - beta.beta_qcd((4, 0), nf), - beta.beta_qcd((5, 0), nf), - ], - [beta.beta_qcd((2, 1), nf), 0, 0, 0], - [0, 0, 0, 0], - ] - ) + # betaQCD = np.array( + # [ + # [ + # beta.beta_qcd((2, 0), nf), + # beta.beta_qcd((3, 0), nf), + # beta.beta_qcd((4, 0), nf), + # beta.beta_qcd((5, 0), nf), + # ], + # [beta.beta_qcd((2, 1), nf), 0, 0, 0], + # [0, 0, 0, 0], + # ] + # ) + betaQCD = np.zeros((4, 4), np.complex_) + for i in range(4): + betaQCD[i, 0] = beta.beta_qcd((i + 2, 0), nf) + betaQCD[0, 1] = beta.beta_qcd((2, 1), nf) for ah in a_steps[1:]: a_half = (ah + al) / 2.0 delta_a = ah - al gamma = np.zeros((2, 2), np.complex_) betatot = 0 - for i in range(0, order[0]): - for j in range(0, order[1]): + for i in range(0, order[0] + 1): + for j in range(0, order[1] + 1): betatot += a_half**2 * betaQCD[i, j] * a_half**i * aem**j gamma += gamma_valence[i, j] * a_half**i * aem**j ln = gamma / betatot * delta_a From dec0642b9f82b223c7decffe83421b8efd73c4d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 7 Jul 2022 16:22:00 +0200 Subject: [PATCH 023/312] Fix gamma_2x2 --- src/eko/anomalous_dimensions/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index 473140c43..2eadaac40 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -212,7 +212,7 @@ def gamma_4x4sector(order, n, nf): @nb.njit(cache=True) -def gamma_4x4sector(order, n, nf): +def gamma_2x2sector(order, n, nf): r""" Computes the tower of the singlet anomalous dimensions matrices @@ -240,7 +240,7 @@ def gamma_4x4sector(order, n, nf): sx = harmonics.sx(n, max_weight=order[0] + 1) gamma_s = np.zeros((order[0] + 1, order[1] + 1, 2, 2), np.complex_) if order[0] >= 1: - gamma_s[1, 0] = as1.gamma_QEDvalence(n, sx[0], nf) + gamma_s[1, 0] = as1.gamma_QEDvalence(n, sx[0]) if order[1] >= 1: gamma_s[0, 1] = aem1.gamma_valence(n, nf, sx[0]) if order[0] >= 1 and order[1] >= 1: From 8cf0499f4d918e4269898ed25681d12f87273551 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 8 Jul 2022 09:46:37 +0200 Subject: [PATCH 024/312] Move exp_4x4 --- src/eko/anomalous_dimensions/__init__.py | 96 ++++++++++++------------ 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index 2eadaac40..ee5628d5b 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -73,6 +73,54 @@ def exp_singlet(gamma_S): return exp, lambda_p, lambda_m, e_p, e_m +@nb.njit(cache=True) +def exp_4x4_sector(gamma_S): + r""" + Computes the exponential and the eigensystem of the singlet anomalous dimension matrix + + Parameters + ---------- + gamma_S : numpy.ndarray + singlet anomalous dimension matrix + + Returns + ------- + exp : numpy.ndarray + exponential of the singlet anomalous dimension matrix :math:`\gamma_{S}(N)` + lambda_p : complex + positive eigenvalue of the singlet anomalous dimension matrix + :math:`\gamma_{S}(N)` + lambda_m : complex + negative eigenvalue of the singlet anomalous dimension matrix + :math:`\gamma_{S}(N)` + e_p : numpy.ndarray + projector for the positive eigenvalue of the singlet anomalous + dimension matrix :math:`\gamma_{S}(N)` + e_m : numpy.ndarray + projector for the negative eigenvalue of the singlet anomalous + dimension matrix :math:`\gamma_{S}(N)` + + See Also + -------- + eko.anomalous_dimensions.as1.gamma_singlet : :math:`\gamma_{S}^{(0)}(N)` + eko.anomalous_dimensions.as2.gamma_singlet : :math:`\gamma_{S}^{(1)}(N)` + eko.anomalous_dimensions.as3.gamma_singlet : :math:`\gamma_{S}^{(2)}(N)` + """ + # compute Matrix of coefficients + w, v = np.linalg.eig(gamma_S) + V = np.transpose(v).dot(v) + C = np.linalg.inv(V) + # compute projectors + tmp = C.dot(np.transpose(v)) + e1 = v[:, 0, np.newaxis].dot(tmp[np.newaxis, 0, :]) + e2 = v[:, 1, np.newaxis].dot(tmp[np.newaxis, 1, :]) + e3 = v[:, 2, np.newaxis].dot(tmp[np.newaxis, 2, :]) + e4 = v[:, 3, np.newaxis].dot(tmp[np.newaxis, 3, :]) + e = np.array([e1, e2, e3, e4]) + exp = sum(e[i] * np.exp(w[i]) for i in range(4)) + return exp, w, e + + @nb.njit(cache=True) def gamma_ns(order, mode, n, nf): r""" @@ -253,51 +301,3 @@ def gamma_2x2sector(order, n, nf): sx = np.append(sx, harmonics.S4(n)) gamma_s[3, 0] = -as3.gamma_QEDvalence(n, nf, sx) return gamma_s - - -@nb.njit(cache=True) -def exp_4x4_sector(gamma_S): - r""" - Computes the exponential and the eigensystem of the singlet anomalous dimension matrix - - Parameters - ---------- - gamma_S : numpy.ndarray - singlet anomalous dimension matrix - - Returns - ------- - exp : numpy.ndarray - exponential of the singlet anomalous dimension matrix :math:`\gamma_{S}(N)` - lambda_p : complex - positive eigenvalue of the singlet anomalous dimension matrix - :math:`\gamma_{S}(N)` - lambda_m : complex - negative eigenvalue of the singlet anomalous dimension matrix - :math:`\gamma_{S}(N)` - e_p : numpy.ndarray - projector for the positive eigenvalue of the singlet anomalous - dimension matrix :math:`\gamma_{S}(N)` - e_m : numpy.ndarray - projector for the negative eigenvalue of the singlet anomalous - dimension matrix :math:`\gamma_{S}(N)` - - See Also - -------- - eko.anomalous_dimensions.as1.gamma_singlet : :math:`\gamma_{S}^{(0)}(N)` - eko.anomalous_dimensions.as2.gamma_singlet : :math:`\gamma_{S}^{(1)}(N)` - eko.anomalous_dimensions.as3.gamma_singlet : :math:`\gamma_{S}^{(2)}(N)` - """ - # compute Matrix of coefficients - w, v = np.linalg.eig(gamma_S) - V = np.transpose(v).dot(v) - C = np.linalg.inv(V) - # compute projectors - tmp = C.dot(np.transpose(v)) - e1 = v[:, 0, np.newaxis].dot(tmp[np.newaxis, 0, :]) - e2 = v[:, 1, np.newaxis].dot(tmp[np.newaxis, 1, :]) - e3 = v[:, 2, np.newaxis].dot(tmp[np.newaxis, 2, :]) - e4 = v[:, 3, np.newaxis].dot(tmp[np.newaxis, 3, :]) - e = np.array([e1, e2, e3, e4]) - exp = sum(e[i] * np.exp(w[i]) for i in range(4)) - return exp, w, e From d6e7a9b1cd86f79e5ce6d8e2dd4d3369ab44b555 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 8 Jul 2022 10:15:01 +0200 Subject: [PATCH 025/312] Init QEDnon_singlet.py --- src/eko/kernels/QEDnon_singlet.py | 46 +++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/eko/kernels/QEDnon_singlet.py diff --git a/src/eko/kernels/QEDnon_singlet.py b/src/eko/kernels/QEDnon_singlet.py new file mode 100644 index 000000000..dc9a1e10f --- /dev/null +++ b/src/eko/kernels/QEDnon_singlet.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +import numba as nb +import numpy as np + +from .. import beta +from . import evolution_integrals as ei +from . import non_singlet, utils + + +@nb.njit(cache=True) +def dispatcher( + order, method, gamma_ns, a1, a0, nf, ev_op_iterations +): # pylint: disable=too-many-return-statements + """ + Determine used kernel and call it. + + In LO we always use the exact solution. + + Parameters + ---------- + order : tuple(int,int) + perturbation order + method : str + method + gamma_ns : numpy.ndarray + non-singlet anomalous dimensions + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + ev_op_iterations : int + number of evolution steps + + Returns + ------- + e_ns : complex + non-singlet EKO + """ + # use always exact in LO + if order[1] == 0: + return non_singlet.dispatcher( + order, method, gamma_ns, a1, a0, nf, ev_op_iterations + ) + raise NotImplementedError("Selected order is not implemented") From 64880910abefb5ea93f876c8517d738ba88957cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Sat, 9 Jul 2022 10:40:25 +0200 Subject: [PATCH 026/312] Implement evolution integrals jm1k --- src/eko/kernels/QEDnon_singlet.py | 2 +- src/eko/kernels/QEDsinglet.py | 15 +++-- src/eko/kernels/evolution_integrals.py | 88 ++++++++++++++++++++++++++ 3 files changed, 97 insertions(+), 8 deletions(-) diff --git a/src/eko/kernels/QEDnon_singlet.py b/src/eko/kernels/QEDnon_singlet.py index dc9a1e10f..7f5808e16 100644 --- a/src/eko/kernels/QEDnon_singlet.py +++ b/src/eko/kernels/QEDnon_singlet.py @@ -41,6 +41,6 @@ def dispatcher( # use always exact in LO if order[1] == 0: return non_singlet.dispatcher( - order, method, gamma_ns, a1, a0, nf, ev_op_iterations + order, method, gamma_ns[0], a1, a0, nf, ev_op_iterations ) raise NotImplementedError("Selected order is not implemented") diff --git a/src/eko/kernels/QEDsinglet.py b/src/eko/kernels/QEDsinglet.py index ae6e66786..27f93cd5f 100644 --- a/src/eko/kernels/QEDsinglet.py +++ b/src/eko/kernels/QEDsinglet.py @@ -10,28 +10,29 @@ @nb.njit(cache=True) def eko_iterate(gamma_singlet, a1, a0, aem, nf, order, ev_op_iterations): - """ - Singlet NLO or NNLO iterated (exact) EKO + """Singlet QEDxQCD iterated (exact) EKO Parameters ---------- gamma_singlet : numpy.ndarray singlet anomalous dimensions matrices a1 : float - target coupling value + target strong coupling value a0 : float - initial coupling value + initial strong coupling value + aem : float + electromagnetic coupling value nf : int number of active flavors - order : int - perturbative order + order : tuple(int,int) + QCDxQED perturbative orders ev_op_iterations : int number of evolution steps Returns ------- e_s^{order} : numpy.ndarray - singlet NLO or NNLO iterated (exact) EKO + singlet QEDxQCD iterated (exact) EKO """ a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) e = np.identity(4, np.complex_) diff --git a/src/eko/kernels/evolution_integrals.py b/src/eko/kernels/evolution_integrals.py index 643f8bf1c..e476047c5 100644 --- a/src/eko/kernels/evolution_integrals.py +++ b/src/eko/kernels/evolution_integrals.py @@ -41,6 +41,32 @@ def j00(a1, a0, nf): return np.log(a1 / a0) / beta.beta_qcd((2, 0), nf) +@nb.njit(cache=True) +def jm10(a1, a0, nf): + r""" + LO-LO exact evolution integral. + + .. math:: + j^{(0,0)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{\beta_0 a_s'} + = \frac{\ln(a_s/a_s^0)}{\beta_0} + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + + Returns + ------- + j00 : float + integral + """ + return (1.0 / a0 - 1.0 / a1) / beta.beta_qcd((2, 0), nf) + + @nb.njit(cache=True) def j11_exact(a1, a0, nf): r""" @@ -147,6 +173,37 @@ def j01_expanded(a1, a0, nf): return j00(a1, a0, nf) - beta.b_qcd((3, 0), nf) * j11_expanded(a1, a0, nf) +@nb.njit(cache=True) +def jm11_exact(a1, a0, nf): + r""" + LO-NLO exact evolution integral. + + .. math:: + j^{(0,1)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s}\!da_s'\, + \frac{a_s'}{\beta_0 a_s'^2 + \beta_1 a_s'^3} + = j^{(0,0)}(a_s,a_s^0) - b_1 j^{(1,1)}(a_s,a_s^0) + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + + Returns + ------- + j11 : float + integral + """ + beta0 = beta.beta_qcd((2, 0), nf) + b1 = beta.b_qcd((3, 0), nf) + return -(1.0 / a1 - 1.0 / a0) / beta0 + b1 / beta0 * ( + np.log(1.0 + 1.0 / (a1 * b1)) - np.log(1.0 + 1.0 / (a0 * b1)) + ) + + @nb.njit(cache=True) def j22_exact(a1, a0, nf): r""" @@ -256,6 +313,37 @@ def j02_exact(a1, a0, nf): ) +@nb.njit(cache=True) +def jm12_exact(a1, a0, nf): + """ + LO-NNLO exact evolution integral. + + .. math:: + j^{(0,2)}(a_s,a_s^0) &= \\int\\limits_{a_s^0}^{a_s}\\!da_s'\\, + \frac{a_s'}{\beta_0 a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ + &= j^{(0,0)}(a_s,a_s^0) - b_1 j^{(1,2)}(a_s,a_s^0) - b_2 j^{(2,2)}(a_s,a_s^0) + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + + Returns + ------- + j02 : complex + integral + """ + return ( + jm10(a1, a0, nf) + - beta.b_qcd((3, 0), nf) * j02_exact(a1, a0, nf) + - beta.b_qcd((4, 0), nf) * j12_exact(a1, a0, nf) + ) + + @nb.njit(cache=True) def j22_expanded(a1, a0, nf): r""" From ad51fe415fb843ea5a8bf91432dfd3016501886d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Sat, 9 Jul 2022 14:14:06 +0200 Subject: [PATCH 027/312] Implement QED non-singlet exact solution in dispatcher --- src/eko/kernels/QEDnon_singlet.py | 182 ++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) diff --git a/src/eko/kernels/QEDnon_singlet.py b/src/eko/kernels/QEDnon_singlet.py index 7f5808e16..dcec9b634 100644 --- a/src/eko/kernels/QEDnon_singlet.py +++ b/src/eko/kernels/QEDnon_singlet.py @@ -7,6 +7,174 @@ from . import non_singlet, utils +@nb.njit(cache=True) +def lo_aem1_exact(gamma_ns, a1, a0, aem, nf): # lo refers to the order in as1 + """ + |LO| non-singlet exact EKO + + Parameters + ---------- + gamma_ns : numpy.ndarray + non-singlet anomalous dimensions + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + + Returns + ------- + e_ns^0 : complex + |LO| non-singlet exact EKO + """ + return np.exp( + (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j00(a1, a0, nf) + + aem * gamma_ns[0, 1] * ei.jm10(a1, a0, nf) + ) + + +@nb.njit(cache=True) +def lo_aem2_exact(gamma_ns, a1, a0, aem, nf): # lo refers to the order in as1 + """ + |LO| non-singlet exact EKO + + Parameters + ---------- + gamma_ns : numpy.ndarray + non-singlet anomalous dimensions + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + + Returns + ------- + e_ns^0 : complex + |LO| non-singlet exact EKO + """ + return np.exp( + (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j00(a1, a0, nf) + + (aem * gamma_ns[0, 1] + aem**2 * gamma_ns[0, 2]) * ei.jm10(a1, a0, nf) + ) + + +@nb.njit(cache=True) +def nlo_aem1_exact(gamma_ns, a1, a0, aem, nf): + """ + |NLO| non-singlet exact EKO + + Parameters + ---------- + gamma_ns : numpy.ndarray + non-singlet anomalous dimensions + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + + Returns + ------- + e_ns^1 : complex + |NLO| non-singlet exact EKO + """ + return np.exp( + (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j01_exact(a1, a0, nf) + + gamma_ns[2, 0] * ei.j11_exact(a1, a0, nf) + + aem * gamma_ns[0, 1] * ei.jm11_exact(a1, a0, nf) + ) + + +@nb.njit(cache=True) +def nlo_aem2_exact(gamma_ns, a1, a0, aem, nf): + """ + |NLO| non-singlet exact EKO + + Parameters + ---------- + gamma_ns : numpy.ndarray + non-singlet anomalous dimensions + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + + Returns + ------- + e_ns^1 : complex + |NLO| non-singlet exact EKO + """ + return np.exp( + (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j01_exact(a1, a0, nf) + + gamma_ns[2, 0] * ei.j11_exact(a1, a0, nf) + + (aem * gamma_ns[0, 1] + aem**2 * gamma_ns[0, 2]) * ei.jm11_exact(a1, a0, nf) + ) + + +@nb.njit(cache=True) +def nnlo_aem1_exact(gamma_ns, a1, a0, aem, nf): + """ + |NNLO| non-singlet exact EKO + + Parameters + ---------- + gamma_ns : numpy.ndarray + non-singlet anomalous dimensions + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + + Returns + ------- + e_ns^2 : complex + |NNLO| non-singlet exact EKO + """ + return np.exp( + (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j02_exact(a1, a0, nf) + + gamma_ns[2, 0] * ei.j12_exact(a1, a0, nf) + + gamma_ns[3, 0] * ei.j22_exact(a1, a0, nf) + + aem * gamma_ns[0, 1] * ei.jm12_exact(a1, a0, nf) + ) + + +@nb.njit(cache=True) +def nnlo_aem2_exact(gamma_ns, a1, a0, aem, nf): + """ + |NNLO| non-singlet exact EKO + + Parameters + ---------- + gamma_ns : numpy.ndarray + non-singlet anomalous dimensions + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + + Returns + ------- + e_ns^2 : complex + |NNLO| non-singlet exact EKO + """ + return np.exp( + (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j02_exact(a1, a0, nf) + + gamma_ns[2, 0] * ei.j12_exact(a1, a0, nf) + + gamma_ns[3, 0] * ei.j22_exact(a1, a0, nf) + + (aem * gamma_ns[0, 1] + aem**2 * gamma_ns[0, 2]) * ei.jm12_exact(a1, a0, nf) + ) + + @nb.njit(cache=True) def dispatcher( order, method, gamma_ns, a1, a0, nf, ev_op_iterations @@ -43,4 +211,18 @@ def dispatcher( return non_singlet.dispatcher( order, method, gamma_ns[0], a1, a0, nf, ev_op_iterations ) + if order[1] == 1: + if order[0] == 1: + return lo_aem1_exact(gamma_ns, a1, a0, nf) + if order[0] == 2: + return nlo_aem1_exact(gamma_ns, a1, a0, nf) + if order[0] == 3: + return nnlo_aem1_exact(gamma_ns, a1, a0, nf) + if order[1] == 2: + if order[0] == 1: + return lo_aem2_exact(gamma_ns, a1, a0, nf) + if order[0] == 2: + return nlo_aem2_exact(gamma_ns, a1, a0, nf) + if order[0] == 3: + return nnlo_aem2_exact(gamma_ns, a1, a0, nf) raise NotImplementedError("Selected order is not implemented") From e525c07cab43ecd66ecc5d7bbfc6d18548fe632a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Sat, 9 Jul 2022 14:18:54 +0200 Subject: [PATCH 028/312] Add missing aem in dispatcher --- src/eko/kernels/QEDnon_singlet.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/eko/kernels/QEDnon_singlet.py b/src/eko/kernels/QEDnon_singlet.py index dcec9b634..a32c03488 100644 --- a/src/eko/kernels/QEDnon_singlet.py +++ b/src/eko/kernels/QEDnon_singlet.py @@ -177,7 +177,7 @@ def nnlo_aem2_exact(gamma_ns, a1, a0, aem, nf): @nb.njit(cache=True) def dispatcher( - order, method, gamma_ns, a1, a0, nf, ev_op_iterations + order, method, gamma_ns, a1, a0, aem, nf, ev_op_iterations ): # pylint: disable=too-many-return-statements """ Determine used kernel and call it. @@ -213,16 +213,16 @@ def dispatcher( ) if order[1] == 1: if order[0] == 1: - return lo_aem1_exact(gamma_ns, a1, a0, nf) + return lo_aem1_exact(gamma_ns, a1, a0, aem, nf) if order[0] == 2: - return nlo_aem1_exact(gamma_ns, a1, a0, nf) + return nlo_aem1_exact(gamma_ns, a1, a0, aem, nf) if order[0] == 3: - return nnlo_aem1_exact(gamma_ns, a1, a0, nf) + return nnlo_aem1_exact(gamma_ns, a1, a0, aem, nf) if order[1] == 2: if order[0] == 1: - return lo_aem2_exact(gamma_ns, a1, a0, nf) + return lo_aem2_exact(gamma_ns, a1, a0, aem, nf) if order[0] == 2: - return nlo_aem2_exact(gamma_ns, a1, a0, nf) + return nlo_aem2_exact(gamma_ns, a1, a0, aem, nf) if order[0] == 3: - return nnlo_aem2_exact(gamma_ns, a1, a0, nf) + return nnlo_aem2_exact(gamma_ns, a1, a0, aem, nf) raise NotImplementedError("Selected order is not implemented") From b14ca77435dff7ba132565b267082672b532e945 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 14 Jul 2022 11:38:18 +0200 Subject: [PATCH 029/312] Implement j_exaact functins for qed --- src/eko/anomalous_dimensions/__init__.py | 6 +- src/eko/kernels/QEDnon_singlet.py | 38 ++-- src/eko/kernels/evolution_integrals.py | 223 ++++++++++++++++++++++- 3 files changed, 238 insertions(+), 29 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index ee5628d5b..8816cafa0 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -241,7 +241,8 @@ def gamma_4x4sector(order, n, nf): eko.anomalous_dimensions.as3.gamma_singlet : :math:`\gamma_{S}^{(2)}(N)` """ # cache the s-es - sx = harmonics.sx(n, max_weight=order[0] + 1) + max_weight = max(order) + sx = harmonics.sx(n, max_weight=max_weight + 1) gamma_s = np.zeros((order[0] + 1, order[1] + 1, 4, 4), np.complex_) if order[0] >= 1: gamma_s[1, 0] = as1.gamma_QEDsinglet(n, sx[0], nf) @@ -285,7 +286,8 @@ def gamma_2x2sector(order, n, nf): eko.anomalous_dimensions.as3.gamma_singlet : :math:`\gamma_{S}^{(2)}(N)` """ # cache the s-es - sx = harmonics.sx(n, max_weight=order[0] + 1) + max_weight = max(order) + sx = harmonics.sx(n, max_weight=max_weight + 1) gamma_s = np.zeros((order[0] + 1, order[1] + 1, 2, 2), np.complex_) if order[0] >= 1: gamma_s[1, 0] = as1.gamma_QEDvalence(n, sx[0]) diff --git a/src/eko/kernels/QEDnon_singlet.py b/src/eko/kernels/QEDnon_singlet.py index a32c03488..5c5129412 100644 --- a/src/eko/kernels/QEDnon_singlet.py +++ b/src/eko/kernels/QEDnon_singlet.py @@ -29,8 +29,8 @@ def lo_aem1_exact(gamma_ns, a1, a0, aem, nf): # lo refers to the order in as1 |LO| non-singlet exact EKO """ return np.exp( - (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j00(a1, a0, nf) - + aem * gamma_ns[0, 1] * ei.jm10(a1, a0, nf) + (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j00_qed(a1, a0, aem, nf) + + aem * gamma_ns[0, 1] * ei.jm10(a1, a0, aem, nf) ) @@ -56,8 +56,8 @@ def lo_aem2_exact(gamma_ns, a1, a0, aem, nf): # lo refers to the order in as1 |LO| non-singlet exact EKO """ return np.exp( - (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j00(a1, a0, nf) - + (aem * gamma_ns[0, 1] + aem**2 * gamma_ns[0, 2]) * ei.jm10(a1, a0, nf) + (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j00_qed(a1, a0, aem, nf) + + (aem * gamma_ns[0, 1] + aem**2 * gamma_ns[0, 2]) * ei.jm10(a1, a0, aem, nf) ) @@ -83,9 +83,9 @@ def nlo_aem1_exact(gamma_ns, a1, a0, aem, nf): |NLO| non-singlet exact EKO """ return np.exp( - (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j01_exact(a1, a0, nf) - + gamma_ns[2, 0] * ei.j11_exact(a1, a0, nf) - + aem * gamma_ns[0, 1] * ei.jm11_exact(a1, a0, nf) + (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j01_exact_qed(a1, a0, aem, nf) + + gamma_ns[2, 0] * ei.j11_exact_qed(a1, a0, aem, nf) + + aem * gamma_ns[0, 1] * ei.jm11_exact(a1, a0, aem, nf) ) @@ -111,9 +111,10 @@ def nlo_aem2_exact(gamma_ns, a1, a0, aem, nf): |NLO| non-singlet exact EKO """ return np.exp( - (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j01_exact(a1, a0, nf) - + gamma_ns[2, 0] * ei.j11_exact(a1, a0, nf) - + (aem * gamma_ns[0, 1] + aem**2 * gamma_ns[0, 2]) * ei.jm11_exact(a1, a0, nf) + (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j01_exact_qed(a1, a0, aem, nf) + + gamma_ns[2, 0] * ei.j11_exact_qed(a1, a0, aem, nf) + + (aem * gamma_ns[0, 1] + aem**2 * gamma_ns[0, 2]) + * ei.jm11_exact(a1, a0, aem, nf) ) @@ -139,10 +140,10 @@ def nnlo_aem1_exact(gamma_ns, a1, a0, aem, nf): |NNLO| non-singlet exact EKO """ return np.exp( - (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j02_exact(a1, a0, nf) - + gamma_ns[2, 0] * ei.j12_exact(a1, a0, nf) - + gamma_ns[3, 0] * ei.j22_exact(a1, a0, nf) - + aem * gamma_ns[0, 1] * ei.jm12_exact(a1, a0, nf) + (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j02_exact_qed(a1, a0, aem, nf) + + gamma_ns[2, 0] * ei.j12_exact_qed(a1, a0, aem, nf) + + gamma_ns[3, 0] * ei.j22_exact_qed(a1, a0, aem, nf) + + aem * gamma_ns[0, 1] * ei.jm12_exact(a1, a0, aem, nf) ) @@ -168,10 +169,11 @@ def nnlo_aem2_exact(gamma_ns, a1, a0, aem, nf): |NNLO| non-singlet exact EKO """ return np.exp( - (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j02_exact(a1, a0, nf) - + gamma_ns[2, 0] * ei.j12_exact(a1, a0, nf) - + gamma_ns[3, 0] * ei.j22_exact(a1, a0, nf) - + (aem * gamma_ns[0, 1] + aem**2 * gamma_ns[0, 2]) * ei.jm12_exact(a1, a0, nf) + (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j02_exact_qed(a1, a0, aem, nf) + + gamma_ns[2, 0] * ei.j12_exact_qed(a1, a0, aem, nf) + + gamma_ns[3, 0] * ei.j22_exact_qed(a1, a0, aem, nf) + + (aem * gamma_ns[0, 1] + aem**2 * gamma_ns[0, 2]) + * ei.jm12_exact(a1, a0, aem, nf) ) diff --git a/src/eko/kernels/evolution_integrals.py b/src/eko/kernels/evolution_integrals.py index e476047c5..4e33212be 100644 --- a/src/eko/kernels/evolution_integrals.py +++ b/src/eko/kernels/evolution_integrals.py @@ -42,7 +42,7 @@ def j00(a1, a0, nf): @nb.njit(cache=True) -def jm10(a1, a0, nf): +def j00_qed(a1, a0, aem, nf): r""" LO-LO exact evolution integral. @@ -64,7 +64,35 @@ def jm10(a1, a0, nf): j00 : float integral """ - return (1.0 / a0 - 1.0 / a1) / beta.beta_qcd((2, 0), nf) + beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) + return np.log(a1 / a0) / beta0 + + +@nb.njit(cache=True) +def jm10(a1, a0, aem, nf): + r""" + LO-LO exact evolution integral. + + .. math:: + j^{(0,0)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{\beta_0 a_s'} + = \frac{\ln(a_s/a_s^0)}{\beta_0} + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + + Returns + ------- + j00 : float + integral + """ + beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) + return (1.0 / a0 - 1.0 / a1) / beta0 @nb.njit(cache=True) @@ -96,6 +124,36 @@ def j11_exact(a1, a0, nf): return (1.0 / beta_qcd_as3) * np.log((1.0 + a1 * b1) / (1.0 + a0 * b1)) +@nb.njit(cache=True) +def j11_exact_qed(a1, a0, aem, nf): + r""" + NLO-NLO exact evolution integral. + + .. math:: + j^{(1,1)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s}\!da_s'\, + \frac{a_s'^2}{\beta_0 a_s'^2 + \beta_1 a_s'^3} + = \frac{1}{\beta_1}\ln\left(\frac{1+b_1 a_s}{1+b_1 a_s^0}\right) + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + + Returns + ------- + j11 : float + integral + """ + beta1 = beta.beta_qcd((3, 0), nf) + beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) + b1 = beta1 / beta0 + return (1.0 / beta1) * np.log((1.0 + a1 * b1) / (1.0 + a0 * b1)) + + @nb.njit(cache=True) def j11_expanded(a1, a0, nf): r""" @@ -148,6 +206,36 @@ def j01_exact(a1, a0, nf): return j00(a1, a0, nf) - beta.b_qcd((3, 0), nf) * j11_exact(a1, a0, nf) +@nb.njit(cache=True) +def j01_exact_qed(a1, a0, aem, nf): + r""" + LO-NLO exact evolution integral. + + .. math:: + j^{(0,1)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s}\!da_s'\, + \frac{a_s'}{\beta_0 a_s'^2 + \beta_1 a_s'^3} + = j^{(0,0)}(a_s,a_s^0) - b_1 j^{(1,1)}(a_s,a_s^0) + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + + Returns + ------- + j11 : float + integral + """ + beta1 = beta.beta_qcd((3, 0), nf) + beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) + b1 = beta1 / beta0 + return j00_qed(a1, a0, aem, nf) - b1 * j11_exact_qed(a1, a0, aem, nf) + + @nb.njit(cache=True) def j01_expanded(a1, a0, nf): r""" @@ -174,7 +262,7 @@ def j01_expanded(a1, a0, nf): @nb.njit(cache=True) -def jm11_exact(a1, a0, nf): +def jm11_exact(a1, a0, aem, nf): r""" LO-NLO exact evolution integral. @@ -197,8 +285,8 @@ def jm11_exact(a1, a0, nf): j11 : float integral """ - beta0 = beta.beta_qcd((2, 0), nf) - b1 = beta.b_qcd((3, 0), nf) + beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) + b1 = beta.beta_qcd((3, 0), nf) / beta0 return -(1.0 / a1 - 1.0 / a0) / beta0 + b1 / beta0 * ( np.log(1.0 + 1.0 / (a1 * b1)) - np.log(1.0 + 1.0 / (a0 * b1)) ) @@ -247,6 +335,50 @@ def j22_exact(a1, a0, nf): ) * np.real(delta / Delta) +@nb.njit(cache=True) +def j22_exact_qed(a1, a0, aem, nf): + r""" + NNLO-NNLO exact evolution integral. + + .. math:: + j^{(2,2)}(a_s,a_s^0) &= + \int\limits_{a_s^0}^{a_s}\!da_s'\,\frac{a_s'^3} + {\beta_0 a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4} + = \frac{1}{\beta_2}\ln\left( + \frac{1 + a_s ( b_1 + b_2 a_s ) }{ 1 + a_s^0 ( b_1 + b_2 a_s^0 )}\right) + - \frac{b_1 \delta}{ \beta_2 \Delta} \\ + \delta &= \atan \left( \frac{b_1 + 2 a_s b_2 }{ \Delta} \right) + - \atan \left( \frac{b_1 + 2 a_s^0 b_2 }{ \Delta} \right) \\ + \Delta &= \sqrt{4 b_2 - b_1^2} + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + + Returns + ------- + j22 : complex + integral + """ + beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) + b1 = beta.beta_qcd((3, 0), nf) / beta0 + b2 = beta.beta_qcd((4, 0), nf) / beta0 + # allow Delta to be complex for nf = 6, the final result will be real + Delta = np.sqrt(complex(4 * b2 - b1**2)) + delta = np.arctan((b1 + 2 * a1 * b2) / Delta) - np.arctan( + (b1 + 2 * a0 * b2) / Delta + ) + log = np.log((1 + a1 * (b1 + b2 * a1)) / (1 + a0 * (b1 + b2 * a0))) + return 1 / (2 * beta.beta_qcd((4, 0), nf)) * log - b1 / ( + beta.beta_qcd((4, 0), nf) + ) * np.real(delta / Delta) + + @nb.njit(cache=True) def j12_exact(a1, a0, nf): r""" @@ -282,6 +414,42 @@ def j12_exact(a1, a0, nf): return 2.0 / (beta.beta_qcd((2, 0), nf)) * np.real(delta / Delta) +@nb.njit(cache=True) +def j12_exact_qed(a1, a0, aem, nf): + r""" + NLO-NNLO exact evolution integral. + + .. math:: + j^{(1,2)}(a_s,a_s^0) &= \int\limits_{a_s^0}^{a_s}\!da_s'\,\frac{a_s'^2}{\beta_0 a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ + &= \frac{2 \delta}{\beta_0 \Delta} \\ + \delta &= \atan \left( \frac{b_1 + 2 a_s b_2 }{ \Delta} \right) - \atan \left( \frac{b_1 + 2 a_s^0 b_2 }{ \Delta} \right) \\ + \Delta &= \sqrt{4 b_2 - b_1^2} + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + + Returns + ------- + j12 : complex + integral + """ # pylint: disable=line-too-long + beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) + b1 = beta.beta_qcd((3, 0), nf) / beta0 + b2 = beta.beta_qcd((4, 0), nf) / beta0 + # allow Delta to be complex for nf = 6, the final result will be real + Delta = np.sqrt(complex(4 * b2 - b1**2)) + delta = np.arctan((b1 + 2 * a1 * b2) / Delta) - np.arctan( + (b1 + 2 * a0 * b2) / Delta + ) + return 2.0 / (beta0) * np.real(delta / Delta) + + @nb.njit(cache=True) def j02_exact(a1, a0, nf): r""" @@ -314,7 +482,41 @@ def j02_exact(a1, a0, nf): @nb.njit(cache=True) -def jm12_exact(a1, a0, nf): +def j02_exact_qed(a1, a0, aem, nf): + r""" + LO-NNLO exact evolution integral. + + .. math:: + j^{(0,2)}(a_s,a_s^0) &= \int\limits_{a_s^0}^{a_s}\!da_s'\, + \frac{a_s'}{\beta_0 a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ + &= j^{(0,0)}(a_s,a_s^0) - b_1 j^{(1,2)}(a_s,a_s^0) - b_2 j^{(2,2)}(a_s,a_s^0) + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + + Returns + ------- + j02 : complex + integral + """ + beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) + b1 = beta.beta_qcd((3, 0), nf) / beta0 + b2 = beta.beta_qcd((4, 0), nf) / beta0 + return ( + j00_qed(a1, a0, aem, nf) + - b1 * j12_exact_qed(a1, a0, aem, nf) + - b2 * j22_exact_qed(a1, a0, aem, nf) + ) + + +@nb.njit(cache=True) +def jm12_exact(a1, a0, aem, nf): """ LO-NNLO exact evolution integral. @@ -337,10 +539,13 @@ def jm12_exact(a1, a0, nf): j02 : complex integral """ + beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) + b1 = beta.beta_qcd((3, 0), nf) / beta0 + b2 = beta.beta_qcd((4, 0), nf) / beta0 return ( - jm10(a1, a0, nf) - - beta.b_qcd((3, 0), nf) * j02_exact(a1, a0, nf) - - beta.b_qcd((4, 0), nf) * j12_exact(a1, a0, nf) + jm10(a1, a0, aem, nf) + - b1 * j02_exact_qed(a1, a0, aem, nf) + - b2 * j12_exact_qed(a1, a0, aem, nf) ) From 5b0aaeb1ecdb8f214bd766f73e418a4b994d563c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 14 Jul 2022 12:40:35 +0200 Subject: [PATCH 030/312] Implement pids for ns+u ns+d ns-u ns-d --- src/eko/basis_rotation.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/eko/basis_rotation.py b/src/eko/basis_rotation.py index d3c89a769..a432e2449 100644 --- a/src/eko/basis_rotation.py +++ b/src/eko/basis_rotation.py @@ -69,7 +69,15 @@ ) """|pid| representation of :data:`evol_basis`.""" -non_singlet_pids_map = {"ns-": 10201, "ns+": 10101, "nsV": 10200} +non_singlet_pids_map = { + "ns-": 10201, + "ns+": 10101, + "nsV": 10200, + "ns-u": 10202, + "ns-d": 10203, + "ns+u": 10102, + "ns+d": 10103, +} singlet_labels = ((100, 100), (100, 21), (21, 100), (21, 21)) non_singlet_labels = ( From b6f4087df624739f06e219a41acb771590857d9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 14 Jul 2022 12:45:29 +0200 Subject: [PATCH 031/312] Implement gamma_ns_qed --- src/eko/anomalous_dimensions/__init__.py | 75 ++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index 8816cafa0..7d1cbc625 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -215,6 +215,81 @@ def gamma_singlet(order, n, nf): return gamma_s +@nb.njit(cache=True) +def gamma_ns_qed(order, mode, n, nf): + r""" + Computes the tower of the non-singlet anomalous dimensions + + Parameters + ---------- + order : tuple(int,int) + perturbative orders + mode : 10201 | 10101 | 10200 + sector identifier + n : complex + Mellin variable + nf : int + Number of active flavors + + Returns + ------- + gamma_ns : numpy.ndarray + non-singlet anomalous dimensions + + See Also + -------- + eko.anomalous_dimensions.as1.gamma_ns : :math:`\gamma_{ns}^{(0)}(N)` + eko.anomalous_dimensions.as2.gamma_nsp : :math:`\gamma_{ns,+}^{(1)}(N)` + eko.anomalous_dimensions.as2.gamma_nsm : :math:`\gamma_{ns,-}^{(1)}(N)` + eko.anomalous_dimensions.as3.gamma_nsp : :math:`\gamma_{ns,+}^{(2)}(N)` + eko.anomalous_dimensions.as3.gamma_nsm : :math:`\gamma_{ns,-}^{(2)}(N)` + eko.anomalous_dimensions.as3.gamma_nsv : :math:`\gamma_{ns,v}^{(2)}(N)` + """ + # cache the s-es + max_weight = max(order) + sx = harmonics.sx(n, max_weight=max_weight + 1) + # now combine + gamma_ns = np.zeros((order[0] + 1, order[1] + 1, 4, 4), np.complex_) + if order[0] >= 1: + gamma_ns[1, 0] = as1.gamma_ns(n, sx[0]) + if order[1] >= 1: + gamma_ns[0, 1] = aem1.gamma_ns(n, sx[0]) + if order[0] >= 1 and order[1] >= 1: + if mode == 10101: + gamma_ns[1, 1] = as1aem1.gamma_nsp(n, sx) + elif mode in [10201, 10200]: + gamma_ns[1, 1] = as1aem1.gamma_nsm(n, sx) + else: + raise NotImplementedError("Non-singlet sector is not implemented") + # NLO and beyond + if order[0] >= 2: + if mode == 10101: + gamma_ns[2, 0] = as2.gamma_nsp(n, nf, sx) + # To fill the full valence vector in NNLO we need to add gamma_ns^1 explicitly here + elif mode in [10201, 10200]: + gamma_ns[2, 0] = as2.gamma_nsm(n, nf, sx) + else: + raise NotImplementedError("Non-singlet sector is not implemented") + if order[1] >= 2: + if mode == 10102: + gamma_ns[0, 2] = aem2.gamma_nspu(n, nf, sx) + if mode == 10103: + gamma_ns[0, 2] = aem2.gamma_nspd(n, nf, sx) + if mode == 10202: + gamma_ns[0, 2] = aem2.gamma_nsmu(n, nf, sx) + if mode == 10203: + gamma_ns[0, 2] = aem2.gamma_nsmd(n, nf, sx) + # NNLO and beyond + if order[0] >= 3: + if mode == 10101: + gamma_ns[3, 0] = -as3.gamma_nsp(n, nf, sx) + elif mode == 10201: + gamma_ns[3, 0] = -as3.gamma_nsm(n, nf, sx) + elif mode == 10200: + gamma_ns[3, 0] = -as3.gamma_nsv(n, nf, sx) + return gamma_ns + + @nb.njit(cache=True) def gamma_4x4sector(order, n, nf): r""" From d10924e79b7c207fae23cc306fff5c45423583cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 14 Jul 2022 15:06:26 +0200 Subject: [PATCH 032/312] Implement gamma_ns_qed --- src/eko/anomalous_dimensions/__init__.py | 37 ++++++++++++++---------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index 7d1cbc625..78ae96fb2 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -21,7 +21,7 @@ import numpy as np from .. import basis_rotation as br -from .. import harmonics +from .. import constants, harmonics from . import aem1, aem2, as1, as1aem1, as2, as3 @@ -253,40 +253,45 @@ def gamma_ns_qed(order, mode, n, nf): if order[0] >= 1: gamma_ns[1, 0] = as1.gamma_ns(n, sx[0]) if order[1] >= 1: - gamma_ns[0, 1] = aem1.gamma_ns(n, sx[0]) + if mode in [10102, 10202]: + gamma_ns[0, 1] = constants.eu2 * aem1.gamma_ns(n, sx[0]) + if mode in [10103, 10203]: + gamma_ns[0, 1] = constants.ed2 * aem1.gamma_ns(n, sx[0]) if order[0] >= 1 and order[1] >= 1: - if mode == 10101: - gamma_ns[1, 1] = as1aem1.gamma_nsp(n, sx) - elif mode in [10201, 10200]: - gamma_ns[1, 1] = as1aem1.gamma_nsm(n, sx) + if mode == 10102: + gamma_ns[1, 1] = constants.eu2 * as1aem1.gamma_nsp(n, sx) + elif mode == 10103: + gamma_ns[1, 1] = constants.ed2 * as1aem1.gamma_nsp(n, sx) + elif mode == 10202: + gamma_ns[1, 1] = constants.eu2 * as1aem1.gamma_nsm(n, sx) + elif mode == 10203: + gamma_ns[1, 1] = constants.ed2 * as1aem1.gamma_nsm(n, sx) else: raise NotImplementedError("Non-singlet sector is not implemented") # NLO and beyond if order[0] >= 2: - if mode == 10101: + if mode in [10102, 10103]: gamma_ns[2, 0] = as2.gamma_nsp(n, nf, sx) # To fill the full valence vector in NNLO we need to add gamma_ns^1 explicitly here - elif mode in [10201, 10200]: + elif mode in [10202, 10203]: gamma_ns[2, 0] = as2.gamma_nsm(n, nf, sx) else: raise NotImplementedError("Non-singlet sector is not implemented") if order[1] >= 2: if mode == 10102: - gamma_ns[0, 2] = aem2.gamma_nspu(n, nf, sx) + gamma_ns[0, 2] = constants.eu2 * aem2.gamma_nspu(n, nf, sx) if mode == 10103: - gamma_ns[0, 2] = aem2.gamma_nspd(n, nf, sx) + gamma_ns[0, 2] = constants.ed2 * aem2.gamma_nspd(n, nf, sx) if mode == 10202: - gamma_ns[0, 2] = aem2.gamma_nsmu(n, nf, sx) + gamma_ns[0, 2] = constants.eu2 * aem2.gamma_nsmu(n, nf, sx) if mode == 10203: - gamma_ns[0, 2] = aem2.gamma_nsmd(n, nf, sx) + gamma_ns[0, 2] = constants.ed2 * aem2.gamma_nsmd(n, nf, sx) # NNLO and beyond if order[0] >= 3: - if mode == 10101: + if mode in [10102, 10103]: gamma_ns[3, 0] = -as3.gamma_nsp(n, nf, sx) - elif mode == 10201: + elif mode in [10202, 10203]: gamma_ns[3, 0] = -as3.gamma_nsm(n, nf, sx) - elif mode == 10200: - gamma_ns[3, 0] = -as3.gamma_nsv(n, nf, sx) return gamma_ns From 603cc0ea2a2ce08018e63314aa21059d0249cdcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 14 Jul 2022 16:10:08 +0200 Subject: [PATCH 033/312] Move computation of charge factors in constants.py --- src/eko/anomalous_dimensions/aem1.py | 37 +++++++++++----------------- src/eko/constants.py | 19 ++++++++++++++ 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/eko/anomalous_dimensions/aem1.py b/src/eko/anomalous_dimensions/aem1.py index 2cd5227a5..3c5b7fa6e 100644 --- a/src/eko/anomalous_dimensions/aem1.py +++ b/src/eko/anomalous_dimensions/aem1.py @@ -103,29 +103,27 @@ def gamma_ns(N, sx): @nb.njit(cache=True) def gamma_singlet(N, nf, sx): - nu = constants.uplike_flavors(nf) - nd = nf - nu - vu = nu / nf - vd = nd / nf - e2avg = (nu * constants.eu2 + nd * constants.ed2) / nf - e2m = constants.eu2 - constants.ed2 - e2delta = (nd * constants.eu2 + nu * constants.ed2) / nf - vue2m = vu * e2m - vde2m = vd * e2m + e2avg = constants.e2avg(nf) + e2delta = constants.vde2m(nf) - constants.vue2m(nf) + constants.e2avg(nf) gamma_S_01 = np.array( [ [0, 0, 0, 0], - [0, gamma_phph(nf), e2avg * gamma_phq(N), vue2m * gamma_phq(N)], + [ + 0, + gamma_phph(nf), + e2avg * gamma_phq(N), + constants.vue2m(nf) * gamma_phq(N), + ], [ 0, e2avg * gamma_qph(N, nf), e2avg * gamma_ns(N, sx), - vue2m * gamma_ns(N, sx), + constants.vue2m(nf) * gamma_ns(N, sx), ], [ 0, - vde2m * gamma_qph(N, nf), - vde2m * gamma_ns(N, sx), + constants.vde2m(nf) * gamma_qph(N, nf), + constants.vde2m(nf) * gamma_ns(N, sx), e2delta * gamma_ns(N, sx), ], ], @@ -136,17 +134,12 @@ def gamma_singlet(N, nf, sx): @nb.njit(cache=True) def gamma_valence(N, nf, sx): - nu = constants.uplike_flavors(nf) - nd = nf - nu - vu = nu / nf - vd = nd / nf - e2avg = (nu * constants.eu2 + nd * constants.ed2) / nf - e2m = constants.eu2 - constants.ed2 - e2delta = (nd * constants.eu2 + nu * constants.ed2) / nf + e2avg = constants.e2avg(nf) + e2delta = constants.vde2m(nf) - constants.vue2m(nf) + constants.e2avg(nf) gamma_V_01 = np.array( [ - [e2avg, vu * e2m], - [vd * e2m, e2delta], + [e2avg, constants.vue2m(nf)], + [constants.vde2m(nf), e2delta], ], np.complex_, ) diff --git a/src/eko/constants.py b/src/eko/constants.py index f74db8e79..0f492d040 100644 --- a/src/eko/constants.py +++ b/src/eko/constants.py @@ -68,3 +68,22 @@ def uplike_flavors(nf): raise NotImplementedError("Selected nf is not implemented") nu = nf // 2 return nu + + +@nb.njit(cache=True) +def e2avg(nf): + nu = uplike_flavors(nf) + nd = nf - nu + return (nu * eu2 + nd * ed2) / nf + + +@nb.njit(cache=True) +def vue2m(nf): + nu = uplike_flavors(nf) + return nu / nf * (eu2 - ed2) + + +@nb.njit(cache=True) +def vde2m(nf): + nd = nf - uplike_flavors(nf) + return nd / nf * (eu2 - ed2) From 079ff5894c7215d67ace89855a1f73f341c64daa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 14 Jul 2022 16:14:25 +0200 Subject: [PATCH 034/312] Move computation of charge factors in constants.py for as1aem1 --- src/eko/anomalous_dimensions/aem2.py | 2 +- src/eko/anomalous_dimensions/as1aem1.py | 28 +++++++++---------------- 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/src/eko/anomalous_dimensions/aem2.py b/src/eko/anomalous_dimensions/aem2.py index a5de99d24..7cdb03941 100644 --- a/src/eko/anomalous_dimensions/aem2.py +++ b/src/eko/anomalous_dimensions/aem2.py @@ -333,7 +333,7 @@ def gamma_singlet(N, nf, sx): nd = nf - nu vu = nu / nf vd = nd / nf - e2avg = (nu * constants.eu2 + nd * constants.ed2) / nf + e2avg = constants.e2avg(nf) e2m = constants.eu2 - constants.ed2 gamma_S_02 = np.array( [ diff --git a/src/eko/anomalous_dimensions/as1aem1.py b/src/eko/anomalous_dimensions/as1aem1.py index 24b15cc0b..bdd35213e 100644 --- a/src/eko/anomalous_dimensions/as1aem1.py +++ b/src/eko/anomalous_dimensions/as1aem1.py @@ -360,16 +360,11 @@ def gamma_nsm(N, sx): @nb.njit(cache=True) def gamma_singlet(N, nf, sx): - nu = constants.uplike_flavors(nf) - nd = nf - nu - vu = nu / nf - vd = nd / nf - e2avg = (nu * constants.eu2 + nd * constants.ed2) / nf + e2avg = constants.e2avg(nf) + e2delta = constants.vde2m(nf) - constants.vue2m(nf) + constants.e2avg(nf) e2_tot = nf * e2avg - e2m = constants.eu2 - constants.ed2 - e2delta = (nd * constants.eu2 + nu * constants.ed2) / nf - vue2m = vu * e2m - vde2m = vd * e2m + vue2m = constants.vue2m(nf) + vde2m = constants.vde2m(nf) gamma_S_11 = np.array( [ [ @@ -404,17 +399,14 @@ def gamma_singlet(N, nf, sx): @nb.njit(cache=True) def gamma_valence(N, nf, sx): - nu = constants.uplike_flavors(nf) - nd = nf - nu - vu = nu / nf - vd = nd / nf - e2avg = (nu * constants.eu2 + nd * constants.ed2) / nf - e2m = constants.eu2 - constants.ed2 - e2delta = (nd * constants.eu2 + nu * constants.ed2) / nf + e2avg = constants.e2avg(nf) + e2delta = constants.vde2m(nf) - constants.vue2m(nf) + constants.e2avg(nf) + vue2m = constants.vue2m(nf) + vde2m = constants.vde2m(nf) gamma_V_11 = np.array( [ - [e2avg, vu * e2m], - [vd * e2m, e2delta], + [e2avg, vue2m], + [vde2m, e2delta], ], np.complex_, ) From 8f8a8a9554c69b067ec5433e297cbb956a9c0831 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 14 Jul 2022 16:19:42 +0200 Subject: [PATCH 035/312] Add docstring to functions in constants.py --- src/eko/constants.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/eko/constants.py b/src/eko/constants.py index 0f492d040..b69bb879a 100644 --- a/src/eko/constants.py +++ b/src/eko/constants.py @@ -72,6 +72,18 @@ def uplike_flavors(nf): @nb.njit(cache=True) def e2avg(nf): + """Computes the charge squared average + + Parameters + ---------- + nf : int + Number of active flavors + + Returns + ------- + e2avg : float + + """ nu = uplike_flavors(nf) nd = nf - nu return (nu * eu2 + nd * ed2) / nf @@ -79,11 +91,35 @@ def e2avg(nf): @nb.njit(cache=True) def vue2m(nf): + """Computes the product nu / nf * (e2u - e2d) + + Parameters + ---------- + nf : int + Number of active flavors + + Returns + ------- + vu * e2m : float + + """ nu = uplike_flavors(nf) return nu / nf * (eu2 - ed2) @nb.njit(cache=True) def vde2m(nf): + """Computes the product nd / nf * (e2u - e2d) + + Parameters + ---------- + nf : int + Number of active flavors + + Returns + ------- + vd * e2m : float + + """ nd = nf - uplike_flavors(nf) return nd / nf * (eu2 - ed2) From 71f50b4c4f651b65267113a0d3f27b37f3578a7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 14 Jul 2022 18:42:48 +0200 Subject: [PATCH 036/312] Make exp_4x4_sector independent on dimension --- src/eko/anomalous_dimensions/__init__.py | 14 +++++++------- src/eko/kernels/QEDsinglet.py | 2 +- src/eko/kernels/QEDvalence.py | 3 +-- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index 78ae96fb2..198d3f1dd 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -74,7 +74,7 @@ def exp_singlet(gamma_S): @nb.njit(cache=True) -def exp_4x4_sector(gamma_S): +def exp_singlet_sector(gamma_S): r""" Computes the exponential and the eigensystem of the singlet anomalous dimension matrix @@ -112,12 +112,12 @@ def exp_4x4_sector(gamma_S): C = np.linalg.inv(V) # compute projectors tmp = C.dot(np.transpose(v)) - e1 = v[:, 0, np.newaxis].dot(tmp[np.newaxis, 0, :]) - e2 = v[:, 1, np.newaxis].dot(tmp[np.newaxis, 1, :]) - e3 = v[:, 2, np.newaxis].dot(tmp[np.newaxis, 2, :]) - e4 = v[:, 3, np.newaxis].dot(tmp[np.newaxis, 3, :]) - e = np.array([e1, e2, e3, e4]) - exp = sum(e[i] * np.exp(w[i]) for i in range(4)) + dim = gamma_S.shape[0] + e = np.zeros((dim, dim, dim), np.complex_) + for i in range(dim): + ei = v[:, i, np.newaxis].dot(tmp[np.newaxis, i, :]) + e[i] = ei + exp = sum(e[i] * np.exp(w[i]) for i in range(dim)) return exp, w, e diff --git a/src/eko/kernels/QEDsinglet.py b/src/eko/kernels/QEDsinglet.py index 27f93cd5f..ad5c41803 100644 --- a/src/eko/kernels/QEDsinglet.py +++ b/src/eko/kernels/QEDsinglet.py @@ -63,7 +63,7 @@ def eko_iterate(gamma_singlet, a1, a0, aem, nf, order, ev_op_iterations): betatot += a_half**2 * betaQCD[i, j] * a_half**i * aem**j gamma += gamma_singlet[i, j] * a_half**i * aem**j ln = gamma / betatot * delta_a - ek = np.ascontiguousarray(ad.exp_4x4_sector(ln)[0]) + ek = np.ascontiguousarray(ad.exp_singlet_sector(ln)[0]) e = ek @ e al = ah return e diff --git a/src/eko/kernels/QEDvalence.py b/src/eko/kernels/QEDvalence.py index b08f0b2ef..6ac55f00c 100644 --- a/src/eko/kernels/QEDvalence.py +++ b/src/eko/kernels/QEDvalence.py @@ -61,8 +61,7 @@ def eko_iterate(gamma_valence, a1, a0, aem, nf, order, ev_op_iterations): betatot += a_half**2 * betaQCD[i, j] * a_half**i * aem**j gamma += gamma_valence[i, j] * a_half**i * aem**j ln = gamma / betatot * delta_a - ek = np.ascontiguousarray(ad.exp_singlet(ln)[0]) - # maybe the name singlet is confusing at this point: better 2x2_sector + ek = np.ascontiguousarray(ad.exp_singlet_sector(ln)[0]) e = ek @ e al = ah return e From 0adf45fe4322f9743100c1456c8c8e4d99f6b003 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 15 Jul 2022 09:15:31 +0200 Subject: [PATCH 037/312] Rename exp_singlet_sector -> exp_matrix --- src/eko/anomalous_dimensions/__init__.py | 2 +- src/eko/kernels/QEDsinglet.py | 2 +- src/eko/kernels/QEDvalence.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index 198d3f1dd..dc20b4945 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -74,7 +74,7 @@ def exp_singlet(gamma_S): @nb.njit(cache=True) -def exp_singlet_sector(gamma_S): +def exp_matrix(gamma_S): r""" Computes the exponential and the eigensystem of the singlet anomalous dimension matrix diff --git a/src/eko/kernels/QEDsinglet.py b/src/eko/kernels/QEDsinglet.py index ad5c41803..0be0265db 100644 --- a/src/eko/kernels/QEDsinglet.py +++ b/src/eko/kernels/QEDsinglet.py @@ -63,7 +63,7 @@ def eko_iterate(gamma_singlet, a1, a0, aem, nf, order, ev_op_iterations): betatot += a_half**2 * betaQCD[i, j] * a_half**i * aem**j gamma += gamma_singlet[i, j] * a_half**i * aem**j ln = gamma / betatot * delta_a - ek = np.ascontiguousarray(ad.exp_singlet_sector(ln)[0]) + ek = np.ascontiguousarray(ad.exp_matrix(ln)[0]) e = ek @ e al = ah return e diff --git a/src/eko/kernels/QEDvalence.py b/src/eko/kernels/QEDvalence.py index 6ac55f00c..0a12ffdc1 100644 --- a/src/eko/kernels/QEDvalence.py +++ b/src/eko/kernels/QEDvalence.py @@ -61,7 +61,7 @@ def eko_iterate(gamma_valence, a1, a0, aem, nf, order, ev_op_iterations): betatot += a_half**2 * betaQCD[i, j] * a_half**i * aem**j gamma += gamma_valence[i, j] * a_half**i * aem**j ln = gamma / betatot * delta_a - ek = np.ascontiguousarray(ad.exp_singlet_sector(ln)[0]) + ek = np.ascontiguousarray(ad.exp_matrix(ln)[0]) e = ek @ e al = ah return e From ad98261d565a4bb0fdda2de37f2b050d08b9a814 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 15 Jul 2022 10:27:48 +0200 Subject: [PATCH 038/312] Implement map_ad_to_intrinsic_evolution and unified_labels --- src/eko/basis_rotation.py | 71 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/src/eko/basis_rotation.py b/src/eko/basis_rotation.py index a432e2449..64d884da4 100644 --- a/src/eko/basis_rotation.py +++ b/src/eko/basis_rotation.py @@ -85,6 +85,38 @@ (non_singlet_pids_map["ns+"], 0), (non_singlet_pids_map["nsV"], 0), ) +# Sdelta = 101 +singlet_unified_labels = ( + (21, 21), + (21, 20), + (21, 100), + (21, 101), + (20, 21), + (20, 20), + (20, 100), + (20, 101), + (100, 21), + (100, 20), + (100, 100), + (100, 101), + (101, 21), + (101, 20), + (101, 100), + (101, 101), +) +# Vdelta = 10204 +valence_unified_labels = ( + (10200, 10200), + (10200, 10204), + (10204, 10200), + (10204, 10204), +) +non_singlet_unified_labels = ( + (non_singlet_pids_map["ns-u"], 0), + (non_singlet_pids_map["ns-d"], 0), + (non_singlet_pids_map["ns+u"], 0), + (non_singlet_pids_map["ns+d"], 0), +) full_labels = (*singlet_labels, *non_singlet_labels) anomalous_dimensions_basis = full_labels r""" @@ -141,6 +173,45 @@ Map anomalous dimension sectors' names to their members """ +map_ad_to_intrinsic_evolution = { + (21, 21): ["g.g"], + (21, 20): ["g.ph"], + (21, 100): ["g.S"], + (21, 101): ["g.Sdelta"], + (20, 21): ["ph.g"], + (20, 20): ["ph.ph"], + (20, 100): ["ph.S"], + (20, 101): ["ph.Sdelta"], + (100, 21): ["S.g"], + (100, 20): ["S.ph"], + (100, 100): ["S.S"], + (100, 101): ["S.Sdelta"], + (101, 21): ["Sdelta.Sdelta"], + (101, 20): ["Sdelta.ph"], + (101, 100): ["Sdelta.S"], + (101, 101): ["Sdelta.Sdelta"], + (10200, 10200): ["V.V"], + (10200, 10204): ["V.Vdelta"], + (10204, 10200): ["Vdelta.V"], + (10204, 10204): ["Vdelta.Vdelta"], + (non_singlet_pids_map["ns+u"], 0): [ + "Tu3.Tu3", + "Tu8.Tu8", + ], + (non_singlet_pids_map["ns+d"], 0): [ + "Td3.Td3", + "Td8.Td8", + ], + (non_singlet_pids_map["ns-u"], 0): [ + "Vu3.Vu3", + "Vu8.Vu8", + ], + (non_singlet_pids_map["ns-d"], 0): [ + "Vd3.Vd3", + "Vd8.Vd8", + ], +} + def ad_projector(ad_lab, nf): """ From 62f624e6b149d4ac2784e27096fb7facb66bbe7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 15 Jul 2022 10:38:43 +0200 Subject: [PATCH 039/312] Fix photon pid --- src/eko/basis_rotation.py | 44 ++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/src/eko/basis_rotation.py b/src/eko/basis_rotation.py index 64d884da4..624ae932f 100644 --- a/src/eko/basis_rotation.py +++ b/src/eko/basis_rotation.py @@ -61,6 +61,22 @@ corresponding |PDF| : :math:`\gamma, \Sigma, g, V, V_{3}, V_{8}, V_{15}, V_{24}, V_{35}, T_{3}, T_{8}, T_{15}, T_{24}, T_{35}` """ +intrinsic_evol_basis = ( + "g", + "ph", + "S", + "Sdelta", + "V", + "Vdelta", + "Td3", + "Vd3", + "Tu3", + "Vu3", + "Td8", + "Vd8", + "Tu8", + "Vu8", +) evol_basis_pids = tuple( [22, 100, 21, 200] @@ -88,19 +104,19 @@ # Sdelta = 101 singlet_unified_labels = ( (21, 21), - (21, 20), + (21, 22), (21, 100), (21, 101), - (20, 21), - (20, 20), - (20, 100), - (20, 101), + (22, 21), + (22, 22), + (22, 100), + (22, 101), (100, 21), - (100, 20), + (100, 22), (100, 100), (100, 101), (101, 21), - (101, 20), + (101, 22), (101, 100), (101, 101), ) @@ -175,19 +191,19 @@ map_ad_to_intrinsic_evolution = { (21, 21): ["g.g"], - (21, 20): ["g.ph"], + (21, 22): ["g.ph"], (21, 100): ["g.S"], (21, 101): ["g.Sdelta"], - (20, 21): ["ph.g"], - (20, 20): ["ph.ph"], - (20, 100): ["ph.S"], - (20, 101): ["ph.Sdelta"], + (22, 21): ["ph.g"], + (22, 22): ["ph.ph"], + (22, 100): ["ph.S"], + (22, 101): ["ph.Sdelta"], (100, 21): ["S.g"], - (100, 20): ["S.ph"], + (100, 22): ["S.ph"], (100, 100): ["S.S"], (100, 101): ["S.Sdelta"], (101, 21): ["Sdelta.Sdelta"], - (101, 20): ["Sdelta.ph"], + (101, 22): ["Sdelta.ph"], (101, 100): ["Sdelta.S"], (101, 101): ["Sdelta.Sdelta"], (10200, 10200): ["V.V"], From b351951660d1d5ec270944fa626fd545d6f7221a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 15 Jul 2022 12:15:11 +0200 Subject: [PATCH 040/312] Implemnt ad_to_uni_evol_map --- src/eko/evolution_operator/physical.py | 70 ++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/eko/evolution_operator/physical.py b/src/eko/evolution_operator/physical.py index aed503520..327f76041 100644 --- a/src/eko/evolution_operator/physical.py +++ b/src/eko/evolution_operator/physical.py @@ -75,6 +75,76 @@ def ad_to_evol_map(cls, op_members, nf, q2_final, intrinsic_range): # map key to MemberName return cls.promote_names(m, q2_final) + @classmethod + def ad_to_uni_evol_map(cls, op_members, nf, q2_final, intrinsic_range): + """ + Obtain map between the 3-dimensional anomalous dimension basis and the + 4-dimensional evolution basis. + + .. todo:: in VFNS sometimes IC is irrelevant if nf>=4 + + Parameters + ---------- + op_members : dict + operator members in anomalous dimension basis + nf : int + number of active light flavors + intrinsic_range : sequence + intrinsic heavy flavors + + Returns + ------- + m : dict + map + """ + # constant elements + m = { + "g.g": op_members[(21, 21)], + "g.ph": op_members[(21, 22)], + "g.S": op_members[(21, 100)], + "g.Sdelta": op_members[(21, 101)], + "ph.g": op_members[(22, 21)], + "ph.ph": op_members[(22, 22)], + "ph.S": op_members[(22, 100)], + "ph.Sdelta": op_members[(22, 101)], + "S.g": op_members[(100, 21)], + "S.ph": op_members[(100, 22)], + "S.S": op_members[(100, 100)], + "S.Sdelta": op_members[(100, 101)], + "V.V": op_members[(10200, 10200)], + "V.Vdelta": op_members[(10200, 10204)], + "Vdelta.V": op_members[(10204, 10200)], + "Vdelta.V": op_members[(10204, 10204)], + } + # add elements which are already active + if nf >= 3: + m["Td3.Td3"] = op_members[(br.non_singlet_pids_map["ns+d"], 0)] + m["Vd3.Vd3"] = op_members[(br.non_singlet_pids_map["ns-d"], 0)] + if nf >= 4: + m["Tu3.Tu3"] = op_members[(br.non_singlet_pids_map["ns+u"], 0)] + m["Vu3.Vu3"] = op_members[(br.non_singlet_pids_map["ns-u"], 0)] + if nf >= 5: + m["Td8.Td8"] = op_members[(br.non_singlet_pids_map["ns+d"], 0)] + m["Vd8.Vd8"] = op_members[(br.non_singlet_pids_map["ns-d"], 0)] + if nf >= 6: + m["Tu8.Tu8"] = op_members[(br.non_singlet_pids_map["ns+u"], 0)] + m["Vu8.Vu8"] = op_members[(br.non_singlet_pids_map["ns-u"], 0)] + # deal with intrinsic heavy quark PDFs + if intrinsic_range is not None: + hqfl = "cbt" + op_id = member.OpMember.id_like( + op_members[(br.non_singlet_pids_map["nsV"], 0)] + ) + for intr_fl in intrinsic_range: + if intr_fl <= nf: # light quarks are not intrinsic + continue + hq = hqfl[intr_fl - 4] # find name + # intrinsic means no evolution, i.e. they are evolving with the identity + m[f"{hq}+.{hq}+"] = op_id.copy() + m[f"{hq}-.{hq}-"] = op_id.copy() + # map key to MemberName + return cls.promote_names(m, q2_final) + def to_flavor_basis_tensor(self): """ Convert the computations into an rank 4 tensor over flavor operator space and From 827383294010e8812d406bb5b3b9090c957506ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 15 Jul 2022 12:40:49 +0200 Subject: [PATCH 041/312] Fix typo intrinsic_evol-basis -> unified_evol_basis --- src/eko/basis_rotation.py | 2 +- src/eko/evolution_operator/physical.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/eko/basis_rotation.py b/src/eko/basis_rotation.py index 624ae932f..3db811a51 100644 --- a/src/eko/basis_rotation.py +++ b/src/eko/basis_rotation.py @@ -61,7 +61,7 @@ corresponding |PDF| : :math:`\gamma, \Sigma, g, V, V_{3}, V_{8}, V_{15}, V_{24}, V_{35}, T_{3}, T_{8}, T_{15}, T_{24}, T_{35}` """ -intrinsic_evol_basis = ( +unified_evol_basis = ( "g", "ph", "S", diff --git a/src/eko/evolution_operator/physical.py b/src/eko/evolution_operator/physical.py index 327f76041..9ce979727 100644 --- a/src/eko/evolution_operator/physical.py +++ b/src/eko/evolution_operator/physical.py @@ -133,7 +133,7 @@ def ad_to_uni_evol_map(cls, op_members, nf, q2_final, intrinsic_range): if intrinsic_range is not None: hqfl = "cbt" op_id = member.OpMember.id_like( - op_members[(br.non_singlet_pids_map["nsV"], 0)] + op_members[(br.non_singlet_pids_map["nsV"], 0)] # Probably it is wrong ) for intr_fl in intrinsic_range: if intr_fl <= nf: # light quarks are not intrinsic From bcac0a896f1a73612aa63a30101772483d0a38e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 15 Jul 2022 12:45:33 +0200 Subject: [PATCH 042/312] Fix typos --- src/eko/basis_rotation.py | 2 +- src/eko/evolution_operator/physical.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/eko/basis_rotation.py b/src/eko/basis_rotation.py index 3db811a51..0889e52da 100644 --- a/src/eko/basis_rotation.py +++ b/src/eko/basis_rotation.py @@ -202,7 +202,7 @@ (100, 22): ["S.ph"], (100, 100): ["S.S"], (100, 101): ["S.Sdelta"], - (101, 21): ["Sdelta.Sdelta"], + (101, 21): ["Sdelta.g"], (101, 22): ["Sdelta.ph"], (101, 100): ["Sdelta.S"], (101, 101): ["Sdelta.Sdelta"], diff --git a/src/eko/evolution_operator/physical.py b/src/eko/evolution_operator/physical.py index 9ce979727..8959961a2 100644 --- a/src/eko/evolution_operator/physical.py +++ b/src/eko/evolution_operator/physical.py @@ -114,7 +114,7 @@ def ad_to_uni_evol_map(cls, op_members, nf, q2_final, intrinsic_range): "V.V": op_members[(10200, 10200)], "V.Vdelta": op_members[(10200, 10204)], "Vdelta.V": op_members[(10204, 10200)], - "Vdelta.V": op_members[(10204, 10204)], + "Vdelta.Vdelta": op_members[(10204, 10204)], } # add elements which are already active if nf >= 3: From 7c05487ba35c7b3167dd28c3c6c10495b5f683d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 15 Jul 2022 14:37:42 +0200 Subject: [PATCH 043/312] Implement QED in quad_ker --- src/eko/evolution_operator/__init__.py | 207 ++++++++++++++++++++----- 1 file changed, 170 insertions(+), 37 deletions(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 0c61144a2..360c57b30 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -19,6 +19,9 @@ from .. import basis_rotation as br from .. import interpolation, mellin from .. import scale_variations as sv +from ..kernels import QEDnon_singlet as qed_ns +from ..kernels import QEDsinglet as qed_s +from ..kernels import QEDvalence as qed_v from ..kernels import non_singlet as ns from ..kernels import singlet as s from ..member import OpMember @@ -50,6 +53,65 @@ def select_singlet_element(ker, mode0, mode1): return ker[k, l] +@nb.njit(cache=True) +def select_QEDsinglet_element(ker, mode0, mode1): + """ + Select element of the singlet matrix + + Parameters + ---------- + ker : numpy.ndarray + singlet integration kernel + mode0 : int + id for first sector element + mode1 : int + id for second sector element + Returns + ------- + ker : complex + singlet integration kernel element + """ + k = 0 + if mode0 == 22: + k = 1 + elif mode0 == 100: + k = 2 + elif mode0 == 101: + k = 3 + l = 0 + if mode1 == 22: + l = 1 + elif mode1 == 100: + l = 2 + elif mode1 == 101: + l = 3 + return ker[k, l] + + +@nb.njit(cache=True) +def select_QEDvalence_element(ker, mode0, mode1): + """ + Select element of the singlet matrix + + Parameters + ---------- + ker : numpy.ndarray + singlet integration kernel + mode0 : int + id for first sector element + mode1 : int + id for second sector element + Returns + ------- + ker : complex + singlet integration kernel element + """ + + k = 0 if mode0 == 100 else 1 + l = 0 if mode1 == 100 else 1 + return ker[k, l] + + spec = [ ("is_singlet", nb.boolean), ("is_log", nb.boolean), @@ -77,6 +139,8 @@ class QuadKerBase: def __init__(self, u, is_log, logx, mode0): self.is_singlet = mode0 in [100, 21, 90] + self.is_QEDsinglet = mode0 in [100, 101, 21, 22] + self.is_QEDvalence = mode0 in [10200, 10204] self.is_log = is_log self.u = u self.logx = logx @@ -128,6 +192,7 @@ def quad_ker( areas, as1, as0, + aem, nf, L, ev_op_iterations, @@ -179,46 +244,114 @@ def quad_ker( integrand = ker_base.integrand(areas) if integrand == 0.0: return 0.0 - - # compute the actual evolution kernel - if ker_base.is_singlet: - gamma_singlet = ad.gamma_singlet(order, ker_base.n, nf) - # scale var exponentiated is directly applied on gamma - if sv_mode == sv.Modes.exponentiated: - gamma_singlet = sv.exponentiated.gamma_variation( - gamma_singlet, order, nf, L + if order[1] == 0: + # compute the actual evolution kernel + if ker_base.is_singlet: + gamma_singlet = ad.gamma_singlet(order, ker_base.n, nf) + # scale var exponentiated is directly applied on gamma + if sv_mode == sv.Modes.exponentiated: + gamma_singlet = sv.exponentiated.gamma_variation( + gamma_singlet, order, nf, L + ) + ker = s.dispatcher( + order, + method, + gamma_singlet, + as1, + as0, + nf, + ev_op_iterations, + ev_op_max_order, ) - ker = s.dispatcher( - order, - method, - gamma_singlet, - as1, - as0, - nf, - ev_op_iterations, - ev_op_max_order, - ) - # scale var expanded is applied on the kernel - if sv_mode == sv.Modes.expanded: - ker = np.ascontiguousarray(ker) @ np.ascontiguousarray( - sv.expanded.singlet_variation(gamma_singlet, as1, order, nf, L) + # scale var expanded is applied on the kernel + if sv_mode == sv.Modes.expanded: + ker = np.ascontiguousarray(ker) @ np.ascontiguousarray( + sv.expanded.singlet_variation(gamma_singlet, as1, order, nf, L) + ) + ker = select_singlet_element(ker, mode0, mode1) + else: + gamma_ns = ad.gamma_ns(order, mode0, ker_base.n, nf) + if sv_mode == sv.Modes.exponentiated: + gamma_ns = sv.exponentiated.gamma_variation(gamma_ns, order, nf, L) + ker = ns.dispatcher( + order, + method, + gamma_ns, + as1, + as0, + nf, + ev_op_iterations, ) - ker = select_singlet_element(ker, mode0, mode1) + if sv_mode == sv.Modes.expanded: + ker = ker * sv.expanded.non_singlet_variation( + gamma_ns, as1, order, nf, L + ) else: - gamma_ns = ad.gamma_ns(order, mode0, ker_base.n, nf) - if sv_mode == sv.Modes.exponentiated: - gamma_ns = sv.exponentiated.gamma_variation(gamma_ns, order, nf, L) - ker = ns.dispatcher( - order, - method, - gamma_ns, - as1, - as0, - nf, - ev_op_iterations, - ) - if sv_mode == sv.Modes.expanded: - ker = ker * sv.expanded.non_singlet_variation(gamma_ns, as1, order, nf, L) + # compute the actual evolution kernel + if ker_base.is_QEDsinglet: + gamma_singlet = ad.gamma_4x4sector(order, ker_base.n, nf) + # scale var exponentiated is directly applied on gamma + if sv_mode == sv.Modes.exponentiated: + gamma_singlet = sv.exponentiated.gamma_variation( + gamma_singlet, order, nf, L + ) + ker = qed_s.dispatcher( + order, + method, + gamma_singlet, + as1, + as0, + aem, + nf, + ev_op_iterations, + ev_op_max_order, + ) + # scale var expanded is applied on the kernel + if sv_mode == sv.Modes.expanded: + ker = np.ascontiguousarray(ker) @ np.ascontiguousarray( + sv.expanded.singlet_variation(gamma_singlet, as1, order, nf, L) + ) + ker = select_QEDsinglet_element(ker, mode0, mode1) + elif ker_base.is_QEDvalence: + gamma_v = ad.gamma_2x2sector(order, mode0, ker_base.n, nf) + # scale var exponentiated is directly applied on gamma + if sv_mode == sv.Modes.exponentiated: + gamma_v = sv.exponentiated.gamma_variation(gamma_v, order, nf, L) + ker = qed_v.dispatcher( + order, + method, + gamma_v, + as1, + as0, + aem, + nf, + ev_op_iterations, + ) + # scale var expanded is applied on the kernel + if sv_mode == sv.Modes.expanded: + ker = np.ascontiguousarray(ker) @ np.ascontiguousarray( + sv.expanded.singlet_variation(gamma_v, as1, order, nf, L) + ) + ker = select_QEDvalence_element(ker, mode0, mode1) + else: + gamma_ns = ad.gamma_ns_qed(order, mode0, ker_base.n, nf) + # scale var exponentiated is directly applied on gamma + if sv_mode == sv.Modes.exponentiated: + gamma_ns = sv.exponentiated.gamma_variation(gamma_ns, order, nf, L) + ker = qed_v.dispatcher( + order, + method, + gamma_ns, + as1, + as0, + aem, + nf, + ev_op_iterations, + ) + if sv_mode == sv.Modes.expanded: + ker = ker * sv.expanded.non_singlet_variation( + gamma_ns, as1, order, nf, L + ) # recombine everthing return np.real(ker * integrand) From e23d218e48f8ff0b75b4a7e23c41e01840292b3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 15 Jul 2022 14:59:27 +0200 Subject: [PATCH 044/312] Add alphaem key to config --- src/eko/evolution_operator/__init__.py | 1 + src/eko/evolution_operator/grid.py | 1 + 2 files changed, 2 insertions(+) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 360c57b30..dd12df99f 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -494,6 +494,7 @@ def quad_ker(self, label, logx, areas): areas=areas, as1=self.a_s[1], as0=self.a_s[0], + aem=self.config["alphaem"], nf=self.nf, L=np.log(self.fact_to_ren), ev_op_iterations=self.config["ev_op_iterations"], diff --git a/src/eko/evolution_operator/grid.py b/src/eko/evolution_operator/grid.py index dbdc05fd8..2097dadc1 100644 --- a/src/eko/evolution_operator/grid.py +++ b/src/eko/evolution_operator/grid.py @@ -111,6 +111,7 @@ def from_dict( """ config = {} config["order"] = tuple(int(o) for o in theory_card["order"]) + config["alphaem"] = theory_card["alphaem"] method = theory_card["ModEv"] mod_ev2method = { "EXA": "iterate-exact", From dc557e7b810c5b1f0e8ba8c8e613224883ce3fa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 15 Jul 2022 15:04:40 +0200 Subject: [PATCH 045/312] Fix test_ev_operator.py --- src/eko/evolution_operator/__init__.py | 2 +- tests/eko/test_ev_operator.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index dd12df99f..260ca58a2 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -494,7 +494,7 @@ def quad_ker(self, label, logx, areas): areas=areas, as1=self.a_s[1], as0=self.a_s[0], - aem=self.config["alphaem"], + aem=self.config["alphaem"] / 4 / np.pi, nf=self.nf, L=np.log(self.fact_to_ren), ev_op_iterations=self.config["ev_op_iterations"], diff --git a/tests/eko/test_ev_operator.py b/tests/eko/test_ev_operator.py index 18828f399..e122a5259 100644 --- a/tests/eko/test_ev_operator.py +++ b/tests/eko/test_ev_operator.py @@ -40,6 +40,7 @@ def test_quad_ker(monkeypatch): areas=np.zeros(3), as1=1, as0=2, + aem=0.00058, nf=3, L=0, ev_op_iterations=0, @@ -58,6 +59,7 @@ def test_quad_ker(monkeypatch): areas=np.zeros(3), as1=1, as0=2, + aem=0.00058, nf=3, L=0, ev_op_iterations=0, @@ -76,6 +78,7 @@ def test_quad_ker(monkeypatch): areas=np.zeros(3), as1=1, as0=2, + aem=0.00058, nf=3, L=0, ev_op_iterations=0, @@ -96,6 +99,7 @@ def test_quad_ker(monkeypatch): areas=np.zeros(3), as1=1, as0=2, + aem=0.00058, nf=3, L=0, ev_op_iterations=0, @@ -116,6 +120,7 @@ def test_quad_ker(monkeypatch): areas=np.zeros(3), as1=1, as0=2, + aem=0.00058, nf=3, L=0, ev_op_iterations=0, @@ -332,6 +337,7 @@ def quad_ker_pegasus( bf.areas_representation, a1, a0, + 0.00058, nf, L, ev_op_iterations, From bd8bba38ca597d94fea909b56b08aae56cb31f7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 15 Jul 2022 15:09:07 +0200 Subject: [PATCH 046/312] Fix typos in quad_ker --- src/eko/evolution_operator/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 260ca58a2..3e33d581a 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -313,7 +313,7 @@ def quad_ker( ) ker = select_QEDsinglet_element(ker, mode0, mode1) elif ker_base.is_QEDvalence: - gamma_v = ad.gamma_2x2sector(order, mode0, ker_base.n, nf) + gamma_v = ad.gamma_2x2sector(order, ker_base.n, nf) # scale var exponentiated is directly applied on gamma if sv_mode == sv.Modes.exponentiated: gamma_v = sv.exponentiated.gamma_variation(gamma_v, order, nf, L) @@ -326,6 +326,7 @@ def quad_ker( aem, nf, ev_op_iterations, + ev_op_max_order, ) # scale var expanded is applied on the kernel if sv_mode == sv.Modes.expanded: @@ -338,7 +339,7 @@ def quad_ker( # scale var exponentiated is directly applied on gamma if sv_mode == sv.Modes.exponentiated: gamma_ns = sv.exponentiated.gamma_variation(gamma_ns, order, nf, L) - ker = qed_v.dispatcher( + ker = qed_ns.dispatcher( order, method, gamma_ns, From f28a36312176f414cda0b3653d631299299c1480 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 15 Jul 2022 16:20:58 +0200 Subject: [PATCH 047/312] Implement qed labels in Operator.labels --- src/eko/basis_rotation.py | 4 +-- src/eko/evolution_operator/__init__.py | 37 +++++++++++++++++--------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/eko/basis_rotation.py b/src/eko/basis_rotation.py index 0889e52da..523377f37 100644 --- a/src/eko/basis_rotation.py +++ b/src/eko/basis_rotation.py @@ -128,10 +128,10 @@ (10204, 10204), ) non_singlet_unified_labels = ( - (non_singlet_pids_map["ns-u"], 0), + (non_singlet_pids_map["ns+d"], 0), (non_singlet_pids_map["ns-d"], 0), (non_singlet_pids_map["ns+u"], 0), - (non_singlet_pids_map["ns+d"], 0), + (non_singlet_pids_map["ns-u"], 0), ) full_labels = (*singlet_labels, *non_singlet_labels) anomalous_dimensions_basis = full_labels diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 3e33d581a..a8a9642ab 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -448,20 +448,31 @@ def labels(self): order = self.config["order"] labels = [] # the NS sector is dynamic - if self.config["debug_skip_non_singlet"]: - logger.warning("%s: skipping non-singlet sector", self.log_label) - else: - # add + as default - labels.append(br.non_singlet_labels[1]) - if order[0] >= 2: # - becomes different starting from NLO - labels.append(br.non_singlet_labels[0]) - if order[0] >= 3: # v also becomes different starting from NNLO - labels.append(br.non_singlet_labels[2]) - # singlet sector is fixed - if self.config["debug_skip_singlet"]: - logger.warning("%s: skipping singlet sector", self.log_label) + if order[1] == 0: + if self.config["debug_skip_non_singlet"]: + logger.warning("%s: skipping non-singlet sector", self.log_label) + else: + # add + as default + labels.append(br.non_singlet_labels[1]) + if order[0] >= 2: # - becomes different starting from NLO + labels.append(br.non_singlet_labels[0]) + if order[0] >= 3: # v also becomes different starting from NNLO + labels.append(br.non_singlet_labels[2]) + # singlet sector is fixed + if self.config["debug_skip_singlet"]: + logger.warning("%s: skipping singlet sector", self.log_label) + else: + labels.extend(br.singlet_labels) else: - labels.extend(br.singlet_labels) + # add +u and +d ad default + labels.append(br.non_singlet_unified_labels[0]) + labels.append(br.non_singlet_unified_labels[2]) + # -u and -d become different starting from O(as1aem1) or O(aem2) + if order[1] >= 2 or order[0] >= 1: + labels.append(br.non_singlet_unified_labels[1]) + labels.append(br.non_singlet_unified_labels[3]) + labels.append(br.valence_unified_labels) + labels.append(br.singlet_unified_labels) return labels def quad_ker(self, label, logx, areas): From 040b91ea42daefc2eccba1356b1688d46d65fc81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 15 Jul 2022 17:25:27 +0200 Subject: [PATCH 048/312] Fix select_QEDvalence_element --- src/eko/evolution_operator/__init__.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index a8a9642ab..d2533622c 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -107,8 +107,8 @@ def select_QEDvalence_element(ker, mode0, mode1): singlet integration kernel element """ - k = 0 if mode0 == 100 else 1 - l = 0 if mode1 == 100 else 1 + k = 0 if mode0 == 10200 else 1 + l = 0 if mode1 == 10200 else 1 return ker[k, l] @@ -497,7 +497,9 @@ def quad_ker(self, label, logx, areas): return functools.partial( quad_ker, # TODO: implement N3LO evolution kernels - order=self.config["order"] if self.config["order"] != 3 else 2, + order=self.config["order"] + if self.config["order"] != 3 + else 2, # TODO : check it mode0=label[0], mode1=label[1], method=self.config["method"], From 4bb36e065b2b55f7bf313e0d458867052e952d88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 18 Jul 2022 11:08:52 +0200 Subject: [PATCH 049/312] Fix order in labels --- src/eko/evolution_operator/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index e5344dfc2..dc0ac2758 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -482,7 +482,7 @@ def labels(self): labels.append(br.non_singlet_unified_labels[0]) labels.append(br.non_singlet_unified_labels[2]) # -u and -d become different starting from O(as1aem1) or O(aem2) - if order[1] >= 2 or order[0] >= 1: + if self.order[1] >= 2 or self.order[0] >= 1: labels.append(br.non_singlet_unified_labels[1]) labels.append(br.non_singlet_unified_labels[3]) labels.append(br.valence_unified_labels) From 2f70118f3763694b41395f998d1a290beeb13ebe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 18 Jul 2022 11:25:07 +0200 Subject: [PATCH 050/312] Rename gamma_4x4sector -> gamma_singlet_qed --- src/eko/anomalous_dimensions/__init__.py | 4 ++-- src/eko/evolution_operator/__init__.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index fbc48cb72..606d9b942 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -336,7 +336,7 @@ def gamma_ns_qed(order, mode, n, nf): @nb.njit(cache=True) -def gamma_4x4sector(order, n, nf): +def gamma_singlet_qed(order, n, nf): r""" Computes the tower of the singlet anomalous dimensions matrices @@ -381,7 +381,7 @@ def gamma_4x4sector(order, n, nf): @nb.njit(cache=True) -def gamma_2x2sector(order, n, nf): +def gamma_valence_qed(order, n, nf): r""" Computes the tower of the singlet anomalous dimensions matrices diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index dc0ac2758..51cc021a8 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -291,7 +291,7 @@ def quad_ker( else: # compute the actual evolution kernel if ker_base.is_QEDsinglet: - gamma_singlet = ad.gamma_4x4sector(order, ker_base.n, nf) + gamma_singlet = ad.gamma_singlet_qed(order, ker_base.n, nf) # scale var exponentiated is directly applied on gamma if sv_mode == sv.Modes.exponentiated: gamma_singlet = sv.exponentiated.gamma_variation( @@ -315,7 +315,7 @@ def quad_ker( ) ker = select_QEDsinglet_element(ker, mode0, mode1) elif ker_base.is_QEDvalence: - gamma_v = ad.gamma_2x2sector(order, ker_base.n, nf) + gamma_v = ad.gamma_valence_qed(order, ker_base.n, nf) # scale var exponentiated is directly applied on gamma if sv_mode == sv.Modes.exponentiated: gamma_v = sv.exponentiated.gamma_variation(gamma_v, order, nf, L) From 233102104d8a0cc12f1c3f04c3f72e40c99c1e5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 18 Jul 2022 15:10:59 +0200 Subject: [PATCH 051/312] Add function to_flavor_basis_tensor_qed --- src/eko/basis_rotation.py | 3 +- src/eko/evolution_operator/__init__.py | 4 +-- src/eko/evolution_operator/grid.py | 6 ++-- src/eko/evolution_operator/physical.py | 42 +++++++++++++++++++++++++- 4 files changed, 48 insertions(+), 7 deletions(-) diff --git a/src/eko/basis_rotation.py b/src/eko/basis_rotation.py index 523377f37..240dd74dd 100644 --- a/src/eko/basis_rotation.py +++ b/src/eko/basis_rotation.py @@ -120,11 +120,10 @@ (101, 100), (101, 101), ) -# Vdelta = 10204 valence_unified_labels = ( (10200, 10200), (10200, 10204), - (10204, 10200), + (10204, 10200), # Vdelta = 10204 (10204, 10204), ) non_singlet_unified_labels = ( diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 51cc021a8..0f98077a3 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -247,7 +247,7 @@ def quad_ker( if integrand == 0.0: return 0.0 if order[1] == 0: - # compute the actual evolution kernel + # compute the actual evolution kernel for pure QCD if ker_base.is_singlet: gamma_singlet = ad.gamma_singlet(order, ker_base.n, nf) # scale var exponentiated is directly applied on gamma @@ -289,7 +289,7 @@ def quad_ker( sv.expanded.non_singlet_variation(gamma_ns, as1, order, nf, L) * ker ) else: - # compute the actual evolution kernel + # compute the actual evolution kernel for QEDxQCD if ker_base.is_QEDsinglet: gamma_singlet = ad.gamma_singlet_qed(order, ker_base.n, nf) # scale var exponentiated is directly applied on gamma diff --git a/src/eko/evolution_operator/grid.py b/src/eko/evolution_operator/grid.py index 2e8317980..141035b0a 100644 --- a/src/eko/evolution_operator/grid.py +++ b/src/eko/evolution_operator/grid.py @@ -295,8 +295,10 @@ def generate(self, q2): flavors.rotate_matching(op.nf + 1), op.q2_to ) final_op = final_op @ rot @ matching @ phys_op - - values, errors = final_op.to_flavor_basis_tensor() + if self.config["order"][1] == 0: + values, errors = final_op.to_flavor_basis_tensor() + else: + values, errors = final_op.to_flavor_basis_tensor_qed() return { "operators": values, "operator_errors": errors, diff --git a/src/eko/evolution_operator/physical.py b/src/eko/evolution_operator/physical.py index 8959961a2..d72bb1d97 100644 --- a/src/eko/evolution_operator/physical.py +++ b/src/eko/evolution_operator/physical.py @@ -133,7 +133,9 @@ def ad_to_uni_evol_map(cls, op_members, nf, q2_final, intrinsic_range): if intrinsic_range is not None: hqfl = "cbt" op_id = member.OpMember.id_like( - op_members[(br.non_singlet_pids_map["nsV"], 0)] # Probably it is wrong + op_members[ + (br.non_singlet_pids_map["nsV"], 0) + ] # TODO : Probably it is wrong ) for intr_fl in intrinsic_range: if intr_fl <= nf: # light quarks are not intrinsic @@ -180,3 +182,41 @@ def to_flavor_basis_tensor(self): :, # input momentum fraction ] += out_weight * (op.error * in_weight) return value_tensor, error_tensor + + def to_flavor_basis_tensor_qed(self): + """ + Convert the computations into an rank 4 tensor over flavor operator space and + momentum fraction operator space. + + Returns + ------- + tensor : numpy.ndarray + EKO + """ + nf_in, nf_out = flavors.get_range(self.op_members.keys()) + len_pids = len(br.flavor_basis_pids) + len_xgrid = list(self.op_members.values())[0].value.shape[0] + # dimension will be pids^2 * xgrid^2 + value_tensor = np.zeros((len_pids, len_xgrid, len_pids, len_xgrid)) + error_tensor = value_tensor.copy() + for name, op in self.op_members.items(): + in_pids = flavors.pids_from_intrinsic_unified_evol(name.input, nf_in, False) + out_pids = flavors.pids_from_intrinsic_unified_evol( + name.target, nf_out, True + ) + for out_idx, out_weight in enumerate(out_pids): + for in_idx, in_weight in enumerate(in_pids): + # keep the outer index to the left as we're multiplying from the right + value_tensor[ + out_idx, # output pid (position) + :, # output momentum fraction + in_idx, # input pid (position) + :, # input momentum fraction + ] += out_weight * (op.value * in_weight) + error_tensor[ + out_idx, # output pid (position) + :, # output momentum fraction + in_idx, # input pid (position) + :, # input momentum fraction + ] += out_weight * (op.error * in_weight) + return value_tensor, error_tensor From 8c777789d92570c1cfb61451304cb2f1d81fe153 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 18 Jul 2022 17:02:55 +0200 Subject: [PATCH 052/312] Implement rotate_flavor_to_unified_evolution --- src/eko/basis_rotation.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/eko/basis_rotation.py b/src/eko/basis_rotation.py index 240dd74dd..736a005e3 100644 --- a/src/eko/basis_rotation.py +++ b/src/eko/basis_rotation.py @@ -163,6 +163,26 @@ Basis rotation matrix between :doc:`Flavor Basis and Evolution Basis `. """ +# Tranformation from physical basis to QCD evolution basis +rotate_flavor_to_unified_evolution = np.array( + [ + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], + [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1], + [0, 1, -1, 1, -1, 1, -1, 0, -1, 1, -1, 1, -1, 1], + [0, -1, -1 - 1, -1, -1, -1, 0, 1, 1, 1, 1, 1, 1], + [0, -1, 1, -1, 1, -1, 1, 0, -1, 1, -1, 1, -1, 1], + [0, 0, 0, 0, -1, 0, 1, 0, 1, 0, -1, 0, 0, 0], + [0, 0, 0, 0, 1, 0, -1, 0, 1, 0, -1, 0, 0, 0], + [0, 0, 0, -1, 0, 1, 0, 0, 0, 1, 0, -1, 0, 0], + [0, 0, 0, 1, 0, -1, 0, 0, 0, 1, 0, -1, 0, 0], + [0, 0, -2, 0, 1, 0, 1, 0, 1, 0, 1, 0, -2, 0], + [0, 0, 2, 0, -1, 0, -1, 0, 1, 0, 1, 0, -2, 0], + [0, -2, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, -2], + [0, 2, 0, -1, 0, -1, 0, 0, 0, 1, 0, 1, 0, -2], + ] +) + map_ad_to_evolution = { (100, 100): ["S.S"], (100, 21): ["S.g"], From cc2e80570df5c267f123e570395d4e93d5762274 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 19 Jul 2022 10:02:47 +0200 Subject: [PATCH 053/312] Add qed in copy_ns_ops --- src/eko/basis_rotation.py | 4 +- src/eko/evolution_operator/__init__.py | 63 ++++++++++++++++++-------- 2 files changed, 48 insertions(+), 19 deletions(-) diff --git a/src/eko/basis_rotation.py b/src/eko/basis_rotation.py index 736a005e3..be3ef42d7 100644 --- a/src/eko/basis_rotation.py +++ b/src/eko/basis_rotation.py @@ -133,6 +133,8 @@ (non_singlet_pids_map["ns-u"], 0), ) full_labels = (*singlet_labels, *non_singlet_labels) +full_unified_labels = (*singlet_unified_labels, *non_singlet_unified_labels) + anomalous_dimensions_basis = full_labels r""" Sorted elements in Anomalous Dimensions Basis as :obj:`str`. @@ -163,7 +165,7 @@ Basis rotation matrix between :doc:`Flavor Basis and Evolution Basis `. """ -# Tranformation from physical basis to QCD evolution basis +# Tranformation from physical basis to QCDxQED evolution basis rotate_flavor_to_unified_evolution = np.array( [ [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 0f98077a3..fae9966c7 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -386,6 +386,7 @@ class Operator(sv.ModeMixin): log_label = "Evolution" # complete list of possible evolution operators labels full_labels = br.full_labels + full_labels_qed = br.full_unified_labels def __init__( self, config, managers, nf, q2_from, q2_to, mellin_cut=5e-2, is_threshold=False @@ -533,15 +534,23 @@ def initialize_op_members(self): np.eye(self.grid_size), np.zeros((self.grid_size, self.grid_size)) ) zero = OpMember(*[np.zeros((self.grid_size, self.grid_size))] * 2) - for n in self.full_labels: - if n in self.labels: - # non-singlet evolution and diagonal op are identities - if n in br.non_singlet_labels or n[0] == n[1]: - self.op_members[n] = eye.copy() + if self.order[1] == 0: + for n in self.full_labels: + if n in self.labels: + # non-singlet evolution and diagonal op are identities + if n in br.non_singlet_labels or n[0] == n[1]: + self.op_members[n] = eye.copy() + else: + self.op_members[n] = zero.copy() else: self.op_members[n] = zero.copy() - else: - self.op_members[n] = zero.copy() + else: + for n in self.full_labels_qed: + if n in self.labels: + if n in br.non_singlet_unified_labels or n[0] == n[1]: + self.op_members[n] = eye.copy() + else: + self.op_members[n] = zero.copy() def run_op_integration( self, @@ -668,22 +677,40 @@ def integrate( def copy_ns_ops(self): """Copy non-singlet kernels, if necessary""" - if self.order[0] == 1: # in LO +=-=v - for label in ["nsV", "ns-"]: + if self.order[1] == 0: + if self.order[0] == 1: # in LO +=-=v + for label in ["nsV", "ns-"]: + self.op_members[ + (br.non_singlet_pids_map[label], 0) + ].value = self.op_members[ + (br.non_singlet_pids_map["ns+"], 0) + ].value.copy() + self.op_members[ + (br.non_singlet_pids_map[label], 0) + ].error = self.op_members[ + (br.non_singlet_pids_map["ns+"], 0) + ].error.copy() + elif self.order[0] == 2: # in NLO -=v self.op_members[ - (br.non_singlet_pids_map[label], 0) + (br.non_singlet_pids_map["nsV"], 0) ].value = self.op_members[ - (br.non_singlet_pids_map["ns+"], 0) + (br.non_singlet_pids_map["ns-"], 0) ].value.copy() self.op_members[ - (br.non_singlet_pids_map[label], 0) + (br.non_singlet_pids_map["nsV"], 0) ].error = self.op_members[ - (br.non_singlet_pids_map["ns+"], 0) + (br.non_singlet_pids_map["ns-"], 0) ].error.copy() - elif self.order[0] == 2: # in NLO -=v + elif self.order[1] == 1: + self.op_members[ + (br.non_singlet_pids_map["ns-u"], 0) + ].value = self.op_members[(br.non_singlet_pids_map["ns+u"], 0)].value.copy() + self.op_members[ + (br.non_singlet_pids_map["ns-u"], 0) + ].error = self.op_members[(br.non_singlet_pids_map["ns+u"], 0)].error.copy() self.op_members[ - (br.non_singlet_pids_map["nsV"], 0) - ].value = self.op_members[(br.non_singlet_pids_map["ns-"], 0)].value.copy() + (br.non_singlet_pids_map["ns-d"], 0) + ].value = self.op_members[(br.non_singlet_pids_map["ns+d"], 0)].value.copy() self.op_members[ - (br.non_singlet_pids_map["nsV"], 0) - ].error = self.op_members[(br.non_singlet_pids_map["ns-"], 0)].error.copy() + (br.non_singlet_pids_map["ns-d"], 0) + ].error = self.op_members[(br.non_singlet_pids_map["ns+d"], 0)].error.copy() From f96cde66a282ed2d4ed85133d5d0f211660cd3e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 19 Jul 2022 11:05:05 +0200 Subject: [PATCH 054/312] Fix bug in rotate_flavor_to_unified_evolution --- src/eko/basis_rotation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eko/basis_rotation.py b/src/eko/basis_rotation.py index be3ef42d7..a768d3bed 100644 --- a/src/eko/basis_rotation.py +++ b/src/eko/basis_rotation.py @@ -172,7 +172,7 @@ [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1], [0, 1, -1, 1, -1, 1, -1, 0, -1, 1, -1, 1, -1, 1], - [0, -1, -1 - 1, -1, -1, -1, 0, 1, 1, 1, 1, 1, 1], + [0, -1, -1, -1, -1, -1, -1, 0, 1, 1, 1, 1, 1, 1], [0, -1, 1, -1, 1, -1, 1, 0, -1, 1, -1, 1, -1, 1], [0, 0, 0, 0, -1, 0, 1, 0, 1, 0, -1, 0, 0, 0], [0, 0, 0, 0, 1, 0, -1, 0, 1, 0, -1, 0, 0, 0], From 5c2ba0c4d5309cebe0f7655204289ef5d160db19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 19 Jul 2022 11:17:55 +0200 Subject: [PATCH 055/312] Add is_QEDsinglet and is_QEDvalence to spec --- src/eko/evolution_operator/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index fae9966c7..d4d0c1541 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -114,6 +114,8 @@ def select_QEDvalence_element(ker, mode0, mode1): spec = [ ("is_singlet", nb.boolean), + ("is_QEDsinglet", nb.boolean), + ("is_QEDvalence", nb.boolean), ("is_log", nb.boolean), ("logx", nb.float64), ("u", nb.float64), From 20bc2cd200b8dfbf3e2de8a59df555b890885fbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 19 Jul 2022 12:49:30 +0200 Subject: [PATCH 056/312] Remove minus sign from as3 --- src/eko/anomalous_dimensions/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index 606d9b942..0f54da823 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -329,9 +329,9 @@ def gamma_ns_qed(order, mode, n, nf): # NNLO and beyond if order[0] >= 3: if mode in [10102, 10103]: - gamma_ns[3, 0] = -as3.gamma_nsp(n, nf, sx) + gamma_ns[3, 0] = as3.gamma_nsp(n, nf, sx) elif mode in [10202, 10203]: - gamma_ns[3, 0] = -as3.gamma_nsm(n, nf, sx) + gamma_ns[3, 0] = as3.gamma_nsm(n, nf, sx) return gamma_ns @@ -376,7 +376,7 @@ def gamma_singlet_qed(order, n, nf): gamma_s[0, 2] = aem2.gamma_singlet(n, nf, sx) if order[0] == 3: sx = np.append(sx, harmonics.S4(n)) - gamma_s[3, 0] = -as3.gamma_QEDsinglet(n, nf, sx) + gamma_s[3, 0] = as3.gamma_QEDsinglet(n, nf, sx) return gamma_s @@ -421,5 +421,5 @@ def gamma_valence_qed(order, n, nf): gamma_s[0, 2] = aem2.gamma_valence(n, nf, sx) if order[0] == 3: sx = np.append(sx, harmonics.S4(n)) - gamma_s[3, 0] = -as3.gamma_QEDvalence(n, nf, sx) + gamma_s[3, 0] = as3.gamma_QEDvalence(n, nf, sx) return gamma_s From b27832f864a6085c6951b36ef05874c8875d2543 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 19 Jul 2022 13:57:18 +0200 Subject: [PATCH 057/312] Test exp_matrix --- tests/eko/test_ad.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/tests/eko/test_ad.py b/tests/eko/test_ad.py index 73a73f6a7..14a93e1cf 100644 --- a/tests/eko/test_ad.py +++ b/tests/eko/test_ad.py @@ -4,11 +4,11 @@ import numpy as np from numpy.testing import assert_allclose, assert_almost_equal, assert_raises +from scipy.linalg import expm from eko import anomalous_dimensions as ad from eko import basis_rotation as br -from eko.anomalous_dimensions import as1 as ad_as1 -from eko.anomalous_dimensions import harmonics +from eko.anomalous_dimensions import as1, harmonics NF = 5 @@ -16,7 +16,7 @@ def test_eigensystem_gamma_singlet_0_values(): n = 3 s1 = harmonics.S1(n) - gamma_S_0 = ad_as1.gamma_singlet(3, s1, NF) + gamma_S_0 = as1.gamma_singlet(3, s1, NF) res = ad.exp_singlet(gamma_S_0) lambda_p = complex(12.273612971466964, 0) lambda_m = complex(5.015275917421917, 0) @@ -35,6 +35,23 @@ def test_eigensystem_gamma_singlet_0_values(): assert_allclose(e_m, res[4]) +def test_exp_matrix(): + n = 3 + s1 = harmonics.S1(n) + gamma_S_0 = as1.gamma_singlet(3, s1, NF) + res = ad.exp_singlet(gamma_S_0)[0] + res2 = ad.exp_matrix(gamma_S_0)[0] + assert_allclose(res, res2) + gamma_S_0_qed = as1.gamma_QEDsinglet(3, s1, NF) + res = expm(gamma_S_0_qed) + res2 = ad.exp_matrix(gamma_S_0_qed)[0] + assert_allclose(res, res2) + gamma_v_0_qed = as1.gamma_QEDvalence(3, s1) + res = expm(gamma_v_0_qed) + res2 = ad.exp_matrix(gamma_v_0_qed)[0] + assert_allclose(res, res2) + + def test_eigensystem_gamma_singlet_projectors_EV(): nf = 3 for N in [3, 4]: # N=2 seems close to 0, so test fails From 01c44cd2b79592f5785087ab9360c284c4d70112 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 19 Jul 2022 14:53:36 +0200 Subject: [PATCH 058/312] Test non single qed --- src/eko/anomalous_dimensions/__init__.py | 10 ++--- tests/eko/test_ad.py | 51 ++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 9 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index 0f54da823..c03a99028 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -289,14 +289,14 @@ def gamma_ns_qed(order, mode, n, nf): max_weight = max(order) sx = harmonics.sx(n, max_weight=max_weight + 1) # now combine - gamma_ns = np.zeros((order[0] + 1, order[1] + 1, 4, 4), np.complex_) + gamma_ns = np.zeros((order[0] + 1, order[1] + 1), np.complex_) if order[0] >= 1: gamma_ns[1, 0] = as1.gamma_ns(n, sx[0]) if order[1] >= 1: if mode in [10102, 10202]: - gamma_ns[0, 1] = constants.eu2 * aem1.gamma_ns(n, sx[0]) + gamma_ns[0, 1] = constants.eu2 * aem1.gamma_ns(n, sx) if mode in [10103, 10203]: - gamma_ns[0, 1] = constants.ed2 * aem1.gamma_ns(n, sx[0]) + gamma_ns[0, 1] = constants.ed2 * aem1.gamma_ns(n, sx) if order[0] >= 1 and order[1] >= 1: if mode == 10102: gamma_ns[1, 1] = constants.eu2 * as1aem1.gamma_nsp(n, sx) @@ -367,7 +367,7 @@ def gamma_singlet_qed(order, n, nf): if order[0] >= 1: gamma_s[1, 0] = as1.gamma_QEDsinglet(n, sx[0], nf) if order[1] >= 1: - gamma_s[0, 1] = aem1.gamma_singlet(n, nf, sx[0]) + gamma_s[0, 1] = aem1.gamma_singlet(n, nf, sx) if order[0] >= 1 and order[1] >= 1: gamma_s[1, 1] = as1aem1.gamma_singlet(n, nf, sx) if order[0] >= 2: @@ -412,7 +412,7 @@ def gamma_valence_qed(order, n, nf): if order[0] >= 1: gamma_s[1, 0] = as1.gamma_QEDvalence(n, sx[0]) if order[1] >= 1: - gamma_s[0, 1] = aem1.gamma_valence(n, nf, sx[0]) + gamma_s[0, 1] = aem1.gamma_valence(n, nf, sx) if order[0] >= 1 and order[1] >= 1: gamma_s[1, 1] = as1aem1.gamma_valence(n, nf, sx) if order[0] >= 2: diff --git a/tests/eko/test_ad.py b/tests/eko/test_ad.py index 14a93e1cf..b7a59c9a8 100644 --- a/tests/eko/test_ad.py +++ b/tests/eko/test_ad.py @@ -72,17 +72,17 @@ def test_eigensystem_gamma_singlet_projectors_EV(): def test_gamma_ns(): nf = 3 - # LO + # as1 assert_almost_equal( ad.gamma_ns((3, 0), br.non_singlet_pids_map["ns+"], 1, nf)[0], 0.0 ) - # NLO + # as2 assert_allclose( ad.gamma_ns((2, 0), br.non_singlet_pids_map["ns-"], 1, nf), np.zeros(2), atol=2e-6, ) - # NNLO + # as3 assert_allclose( ad.gamma_ns((3, 0), br.non_singlet_pids_map["ns-"], 1, nf), np.zeros(3), @@ -93,7 +93,7 @@ def test_gamma_ns(): np.zeros(3), atol=8e-4, ) - # N3LO + # as4 assert_allclose( ad.gamma_ns((4, 0), br.non_singlet_pids_map["ns-"], 1, nf), np.zeros(4), @@ -111,3 +111,46 @@ def test_gamma_ns(): ad.gamma_ns((4, 0), br.non_singlet_pids_map["ns+"], 1, nf), np.zeros(4), ) + + +def test_gamma_ns_qed(): + nf = 3 + # aem1 + assert_almost_equal( + ad.gamma_ns_qed((0, 1), br.non_singlet_pids_map["ns-u"], 1, nf), + np.zeros((1, 2)), + ) + assert_almost_equal( + ad.gamma_ns_qed((0, 1), br.non_singlet_pids_map["ns-d"], 1, nf), + np.zeros((1, 2)), + ) + assert_almost_equal( + ad.gamma_ns_qed((0, 1), br.non_singlet_pids_map["ns+u"], 1, nf), + np.zeros((1, 2)), + ) + assert_almost_equal( + ad.gamma_ns_qed((0, 1), br.non_singlet_pids_map["ns+d"], 1, nf), + np.zeros((1, 2)), + ) + # as1aem1 + assert_almost_equal( + ad.gamma_ns_qed((1, 2), br.non_singlet_pids_map["ns-u"], 1, nf), + np.zeros((2, 3)), + decimal=5, + ) + assert_almost_equal( + ad.gamma_ns_qed((1, 2), br.non_singlet_pids_map["ns-d"], 1, nf), + np.zeros((2, 3)), + decimal=5, + ) + # aem2 + assert_almost_equal( + ad.gamma_ns_qed((0, 2), br.non_singlet_pids_map["ns-u"], 1, nf), + np.zeros((1, 3)), + decimal=5, + ) + assert_almost_equal( + ad.gamma_ns_qed((0, 2), br.non_singlet_pids_map["ns-d"], 1, nf), + np.zeros((1, 3)), + decimal=5, + ) From 5cd2da6cdb044f144942aa12ccbc0cd314dbb57f Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Tue, 19 Jul 2022 16:37:59 +0200 Subject: [PATCH 059/312] Make pure QCD back running --- src/eko/anomalous_dimensions/__init__.py | 43 ++++++++++++++---------- src/eko/evolution_operator/__init__.py | 42 ++++++++++++----------- 2 files changed, 47 insertions(+), 38 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index c03a99028..2bb47d10d 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -108,16 +108,22 @@ def exp_matrix(gamma_S): """ # compute Matrix of coefficients w, v = np.linalg.eig(gamma_S) - V = np.transpose(v).dot(v) + vT = np.transpose(v) + V = vT.dot(v) C = np.linalg.inv(V) # compute projectors - tmp = C.dot(np.transpose(v)) + tmp = C.dot(vT) dim = gamma_S.shape[0] e = np.zeros((dim, dim, dim), np.complex_) + # TODO check if this loop can be entirely cast to numpy for i in range(dim): - ei = v[:, i, np.newaxis].dot(tmp[np.newaxis, i, :]) - e[i] = ei - exp = sum(e[i] * np.exp(w[i]) for i in range(dim)) + e[i] = np.outer(vT[i], np.transpose(tmp)[i]) + # TODO use correct np call + # exp = sum(e[i] * np.exp(w[i]) for i in range(dim)) + # exp = np.sum(e * np.exp(w), axis=0) + exp = np.zeros((dim, dim), np.complex_) + for i in range(dim): + exp += e[i] * np.exp(w[i]) return exp, w, e @@ -364,19 +370,20 @@ def gamma_singlet_qed(order, n, nf): max_weight = max(order) sx = harmonics.sx(n, max_weight=max_weight + 1) gamma_s = np.zeros((order[0] + 1, order[1] + 1, 4, 4), np.complex_) - if order[0] >= 1: - gamma_s[1, 0] = as1.gamma_QEDsinglet(n, sx[0], nf) - if order[1] >= 1: - gamma_s[0, 1] = aem1.gamma_singlet(n, nf, sx) - if order[0] >= 1 and order[1] >= 1: - gamma_s[1, 1] = as1aem1.gamma_singlet(n, nf, sx) - if order[0] >= 2: - gamma_s[2, 0] = as2.gamma_QEDsinglet(n, nf, sx) - if order[1] >= 2: - gamma_s[0, 2] = aem2.gamma_singlet(n, nf, sx) - if order[0] == 3: - sx = np.append(sx, harmonics.S4(n)) - gamma_s[3, 0] = as3.gamma_QEDsinglet(n, nf, sx) + # TODO print(as1.gamma_QEDsinglet(n, sx[0], nf).shape) + # if order[0] >= 1: + # gamma_s[1, 0] = as1.gamma_QEDsinglet(n, sx[0], nf) + # if order[1] >= 1: + # gamma_s[0, 1] = aem1.gamma_singlet(n, nf, sx) + # if order[0] >= 1 and order[1] >= 1: + # gamma_s[1, 1] = as1aem1.gamma_singlet(n, nf, sx) + # if order[0] >= 2: + # gamma_s[2, 0] = as2.gamma_QEDsinglet(n, nf, sx) + # if order[1] >= 2: + # gamma_s[0, 2] = aem2.gamma_singlet(n, nf, sx) + # if order[0] == 3: + # sx = np.append(sx, harmonics.S4(n)) + # gamma_s[3, 0] = as3.gamma_QEDsinglet(n, nf, sx) return gamma_s diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index d4d0c1541..fad869adc 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -248,6 +248,7 @@ def quad_ker( integrand = ker_base.integrand(areas) if integrand == 0.0: return 0.0 + # TODO refactor calls if order[1] == 0: # compute the actual evolution kernel for pure QCD if ker_base.is_singlet: @@ -294,11 +295,12 @@ def quad_ker( # compute the actual evolution kernel for QEDxQCD if ker_base.is_QEDsinglet: gamma_singlet = ad.gamma_singlet_qed(order, ker_base.n, nf) + # TODO check scale variations # scale var exponentiated is directly applied on gamma - if sv_mode == sv.Modes.exponentiated: - gamma_singlet = sv.exponentiated.gamma_variation( - gamma_singlet, order, nf, L - ) + # if sv_mode == sv.Modes.exponentiated: + # gamma_singlet = sv.exponentiated.gamma_variation( + # gamma_singlet, order, nf, L + # ) ker = qed_s.dispatcher( order, method, @@ -311,16 +313,16 @@ def quad_ker( ev_op_max_order, ) # scale var expanded is applied on the kernel - if sv_mode == sv.Modes.expanded and not is_threshold: - ker = np.ascontiguousarray(ker) @ np.ascontiguousarray( - sv.expanded.singlet_variation(gamma_singlet, as1, order, nf, L) - ) + # if sv_mode == sv.Modes.expanded and not is_threshold: + # ker = np.ascontiguousarray(ker) @ np.ascontiguousarray( + # sv.expanded.singlet_variation(gamma_singlet, as1, order, nf, L) + # ) ker = select_QEDsinglet_element(ker, mode0, mode1) elif ker_base.is_QEDvalence: gamma_v = ad.gamma_valence_qed(order, ker_base.n, nf) # scale var exponentiated is directly applied on gamma - if sv_mode == sv.Modes.exponentiated: - gamma_v = sv.exponentiated.gamma_variation(gamma_v, order, nf, L) + # if sv_mode == sv.Modes.exponentiated: + # gamma_v = sv.exponentiated.gamma_variation(gamma_v, order, nf, L) ker = qed_v.dispatcher( order, method, @@ -333,16 +335,16 @@ def quad_ker( ev_op_max_order, ) # scale var expanded is applied on the kernel - if sv_mode == sv.Modes.expanded and not is_threshold: - ker = np.ascontiguousarray( - sv.expanded.singlet_variation(gamma_v, as1, order, nf, L) - ) @ np.ascontiguousarray(ker) + # if sv_mode == sv.Modes.expanded and not is_threshold: + # ker = np.ascontiguousarray( + # sv.expanded.singlet_variation(gamma_v, as1, order, nf, L) + # ) @ np.ascontiguousarray(ker) ker = select_QEDvalence_element(ker, mode0, mode1) else: gamma_ns = ad.gamma_ns_qed(order, mode0, ker_base.n, nf) # scale var exponentiated is directly applied on gamma - if sv_mode == sv.Modes.exponentiated: - gamma_ns = sv.exponentiated.gamma_variation(gamma_ns, order, nf, L) + # if sv_mode == sv.Modes.exponentiated: + # gamma_ns = sv.exponentiated.gamma_variation(gamma_ns, order, nf, L) ker = qed_ns.dispatcher( order, method, @@ -353,10 +355,10 @@ def quad_ker( nf, ev_op_iterations, ) - if sv_mode == sv.Modes.expanded and not is_threshold: - ker = ( - sv.expanded.non_singlet_variation(gamma_ns, as1, order, nf, L) * ker - ) + # if sv_mode == sv.Modes.expanded and not is_threshold: + # ker = ( + # sv.expanded.non_singlet_variation(gamma_ns, as1, order, nf, L) * ker + # ) # recombine everything return np.real(ker * integrand) From 1767e75c7c418508f3fa64789c270a4e9181d846 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 19 Jul 2022 17:12:29 +0200 Subject: [PATCH 060/312] Fix test_exp_matrix --- src/eko/anomalous_dimensions/__init__.py | 28 ++++++++++++------------ 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index 2bb47d10d..adf286bd2 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -117,7 +117,7 @@ def exp_matrix(gamma_S): e = np.zeros((dim, dim, dim), np.complex_) # TODO check if this loop can be entirely cast to numpy for i in range(dim): - e[i] = np.outer(vT[i], np.transpose(tmp)[i]) + e[i] = np.outer(vT[i], tmp[i]) # TODO use correct np call # exp = sum(e[i] * np.exp(w[i]) for i in range(dim)) # exp = np.sum(e * np.exp(w), axis=0) @@ -371,19 +371,19 @@ def gamma_singlet_qed(order, n, nf): sx = harmonics.sx(n, max_weight=max_weight + 1) gamma_s = np.zeros((order[0] + 1, order[1] + 1, 4, 4), np.complex_) # TODO print(as1.gamma_QEDsinglet(n, sx[0], nf).shape) - # if order[0] >= 1: - # gamma_s[1, 0] = as1.gamma_QEDsinglet(n, sx[0], nf) - # if order[1] >= 1: - # gamma_s[0, 1] = aem1.gamma_singlet(n, nf, sx) - # if order[0] >= 1 and order[1] >= 1: - # gamma_s[1, 1] = as1aem1.gamma_singlet(n, nf, sx) - # if order[0] >= 2: - # gamma_s[2, 0] = as2.gamma_QEDsinglet(n, nf, sx) - # if order[1] >= 2: - # gamma_s[0, 2] = aem2.gamma_singlet(n, nf, sx) - # if order[0] == 3: - # sx = np.append(sx, harmonics.S4(n)) - # gamma_s[3, 0] = as3.gamma_QEDsinglet(n, nf, sx) + if order[0] >= 1: + gamma_s[1, 0] = as1.gamma_QEDsinglet(n, sx[0], nf) + if order[1] >= 1: + gamma_s[0, 1] = aem1.gamma_singlet(n, nf, sx) + if order[0] >= 1 and order[1] >= 1: + gamma_s[1, 1] = as1aem1.gamma_singlet(n, nf, sx) + if order[0] >= 2: + gamma_s[2, 0] = as2.gamma_QEDsinglet(n, nf, sx) + if order[1] >= 2: + gamma_s[0, 2] = aem2.gamma_singlet(n, nf, sx) + if order[0] == 3: + sx = np.append(sx, harmonics.S4(n)) + gamma_s[3, 0] = as3.gamma_QEDsinglet(n, nf, sx) return gamma_s From 48221668ff377cc9d8782804aa99f22768e26118 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 20 Jul 2022 10:38:42 +0200 Subject: [PATCH 061/312] Rename gamma_s -> gamma_v in gamma_valence_qed --- src/eko/anomalous_dimensions/__init__.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index adf286bd2..f523945d6 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -415,18 +415,18 @@ def gamma_valence_qed(order, n, nf): # cache the s-es max_weight = max(order) sx = harmonics.sx(n, max_weight=max_weight + 1) - gamma_s = np.zeros((order[0] + 1, order[1] + 1, 2, 2), np.complex_) + gamma_v = np.zeros((order[0] + 1, order[1] + 1, 2, 2), np.complex_) if order[0] >= 1: - gamma_s[1, 0] = as1.gamma_QEDvalence(n, sx[0]) + gamma_v[1, 0] = as1.gamma_QEDvalence(n, sx[0]) if order[1] >= 1: - gamma_s[0, 1] = aem1.gamma_valence(n, nf, sx) + gamma_v[0, 1] = aem1.gamma_valence(n, nf, sx) if order[0] >= 1 and order[1] >= 1: - gamma_s[1, 1] = as1aem1.gamma_valence(n, nf, sx) + gamma_v[1, 1] = as1aem1.gamma_valence(n, nf, sx) if order[0] >= 2: - gamma_s[2, 0] = as2.gamma_QEDvalence(n, nf, sx) + gamma_v[2, 0] = as2.gamma_QEDvalence(n, nf, sx) if order[1] >= 2: - gamma_s[0, 2] = aem2.gamma_valence(n, nf, sx) + gamma_v[0, 2] = aem2.gamma_valence(n, nf, sx) if order[0] == 3: sx = np.append(sx, harmonics.S4(n)) - gamma_s[3, 0] = as3.gamma_QEDvalence(n, nf, sx) - return gamma_s + gamma_v[3, 0] = as3.gamma_QEDvalence(n, nf, sx) + return gamma_v From 1ec49c3094bcc55ea97ed0cdd6743ad7e4d690d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 20 Jul 2022 11:36:41 +0200 Subject: [PATCH 062/312] Implement expanded.gamma_variation_qed --- src/eko/anomalous_dimensions/__init__.py | 1 - src/eko/evolution_operator/__init__.py | 20 +++---- src/eko/scale_variations/exponentiated.py | 72 +++++++++++++++++++++++ 3 files changed, 81 insertions(+), 12 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index f523945d6..4948a823f 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -370,7 +370,6 @@ def gamma_singlet_qed(order, n, nf): max_weight = max(order) sx = harmonics.sx(n, max_weight=max_weight + 1) gamma_s = np.zeros((order[0] + 1, order[1] + 1, 4, 4), np.complex_) - # TODO print(as1.gamma_QEDsinglet(n, sx[0], nf).shape) if order[0] >= 1: gamma_s[1, 0] = as1.gamma_QEDsinglet(n, sx[0], nf) if order[1] >= 1: diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index fad869adc..c9fb31ad7 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -294,17 +294,15 @@ def quad_ker( else: # compute the actual evolution kernel for QEDxQCD if ker_base.is_QEDsinglet: - gamma_singlet = ad.gamma_singlet_qed(order, ker_base.n, nf) + gamma_s = ad.gamma_singlet_qed(order, ker_base.n, nf) # TODO check scale variations # scale var exponentiated is directly applied on gamma - # if sv_mode == sv.Modes.exponentiated: - # gamma_singlet = sv.exponentiated.gamma_variation( - # gamma_singlet, order, nf, L - # ) + if sv_mode == sv.Modes.exponentiated: + gamma_s = sv.exponentiated.gamma_variation_qed(gamma_s, order, nf, L) ker = qed_s.dispatcher( order, method, - gamma_singlet, + gamma_s, as1, as0, aem, @@ -315,14 +313,14 @@ def quad_ker( # scale var expanded is applied on the kernel # if sv_mode == sv.Modes.expanded and not is_threshold: # ker = np.ascontiguousarray(ker) @ np.ascontiguousarray( - # sv.expanded.singlet_variation(gamma_singlet, as1, order, nf, L) + # sv.expanded.singlet_variation(gamma_s, as1, order, nf, L) # ) ker = select_QEDsinglet_element(ker, mode0, mode1) elif ker_base.is_QEDvalence: gamma_v = ad.gamma_valence_qed(order, ker_base.n, nf) # scale var exponentiated is directly applied on gamma - # if sv_mode == sv.Modes.exponentiated: - # gamma_v = sv.exponentiated.gamma_variation(gamma_v, order, nf, L) + if sv_mode == sv.Modes.exponentiated: + gamma_v = sv.exponentiated.gamma_variation_qed(gamma_v, order, nf, L) ker = qed_v.dispatcher( order, method, @@ -343,8 +341,8 @@ def quad_ker( else: gamma_ns = ad.gamma_ns_qed(order, mode0, ker_base.n, nf) # scale var exponentiated is directly applied on gamma - # if sv_mode == sv.Modes.exponentiated: - # gamma_ns = sv.exponentiated.gamma_variation(gamma_ns, order, nf, L) + if sv_mode == sv.Modes.exponentiated: + gamma_ns = sv.exponentiated.gamma_variation_qed(gamma_ns, order, nf, L) ker = qed_ns.dispatcher( order, method, diff --git a/src/eko/scale_variations/exponentiated.py b/src/eko/scale_variations/exponentiated.py index 34d4feaea..40e1ff61f 100644 --- a/src/eko/scale_variations/exponentiated.py +++ b/src/eko/scale_variations/exponentiated.py @@ -6,6 +6,32 @@ from .. import beta +# @nb.njit(cache=True) +# def gamma_variation(gamma, order, nf, L): +# """ +# Adjust the anomalous dimensions with the scale variations. + +# Parameters +# ---------- +# gamma : numpy.ndarray +# anomalous dimensions +# order : tuple(int,int) +# perturbation order +# nf : int +# number of active flavors +# L : float +# logarithmic ratio of factorization and renormalization scale + +# Returns +# ------- +# gamma : numpy.ndarray +# adjusted anomalous dimensions +# """ +# if order[1] == 0 : +# return gamma_variation_pure_qcd(gamma, order, nf, L) +# else : +# return gamma_variation_qed(gamma, order, nf, L) + @nb.njit(cache=True) def gamma_variation(gamma, order, nf, L): @@ -50,3 +76,49 @@ def gamma_variation(gamma, order, nf, L): if order[0] >= 2: gamma[1] -= beta0 * gamma[0] * L return gamma + + +@nb.njit(cache=True) +def gamma_variation_qed(gamma, order, nf, L): + """ + Adjust the anomalous dimensions with the scale variations. + + Parameters + ---------- + gamma : numpy.ndarray + anomalous dimensions + order : tuple(int,int) + perturbation order + nf : int + number of active flavors + L : float + logarithmic ratio of factorization and renormalization scale + + Returns + ------- + gamma : numpy.ndarray + adjusted anomalous dimensions + """ + # since we are modifying *in-place* be carefull, that the order matters! + # and indeed, we need to adjust the high elements first + beta0 = beta.beta_qcd((2, 0), nf) + beta1 = beta.beta_qcd((3, 0), nf) + if order[0] >= 4: + gamma[4, 0] -= ( + 3 * beta0 * L * gamma[3, 0] + + (2 * beta1 * L - 3 * beta0**2 * L**2) * gamma[2, 0] + + ( + beta.beta_qcd((4, 0), nf) * L + - 5 / 2 * beta1 * beta0 * L**2 + + beta0**3 * L**3 + ) + * gamma[1, 0] + ) + if order[0] >= 3: + gamma[3, 0] -= ( + 2 * beta0 * gamma[2, 0] * L + + (beta1 * L - beta0**2 * L**2) * gamma[1, 0] + ) + if order[0] >= 2: + gamma[2, 0] -= beta0 * gamma[1, 0] * L + return gamma From d97ab6fc6f1b31cf1ed0555650610d91c25cd1e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 20 Jul 2022 12:28:15 +0200 Subject: [PATCH 063/312] Test some math properties of exp_matrix --- tests/eko/test_ad.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/eko/test_ad.py b/tests/eko/test_ad.py index b7a59c9a8..e3b6704c4 100644 --- a/tests/eko/test_ad.py +++ b/tests/eko/test_ad.py @@ -50,6 +50,11 @@ def test_exp_matrix(): res = expm(gamma_v_0_qed) res2 = ad.exp_matrix(gamma_v_0_qed)[0] assert_allclose(res, res2) + diag = np.diag([1, 2, 3, 4]) + assert_allclose(np.diag(np.exp([1, 2, 3, 4])), ad.exp_matrix(diag)[0]) + Id = np.identity(4, np.complex_) + Zero = np.zeros((4, 4), np.complex_) + assert_allclose(Id, ad.exp_matrix(Zero)[0]) def test_eigensystem_gamma_singlet_projectors_EV(): From d8b7b24b803b4d179be9a595e9e57f5871681c8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 20 Jul 2022 12:28:51 +0200 Subject: [PATCH 064/312] Comment gamma_singlet_qed --- src/eko/anomalous_dimensions/__init__.py | 26 ++++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index 4948a823f..eaf406060 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -370,19 +370,19 @@ def gamma_singlet_qed(order, n, nf): max_weight = max(order) sx = harmonics.sx(n, max_weight=max_weight + 1) gamma_s = np.zeros((order[0] + 1, order[1] + 1, 4, 4), np.complex_) - if order[0] >= 1: - gamma_s[1, 0] = as1.gamma_QEDsinglet(n, sx[0], nf) - if order[1] >= 1: - gamma_s[0, 1] = aem1.gamma_singlet(n, nf, sx) - if order[0] >= 1 and order[1] >= 1: - gamma_s[1, 1] = as1aem1.gamma_singlet(n, nf, sx) - if order[0] >= 2: - gamma_s[2, 0] = as2.gamma_QEDsinglet(n, nf, sx) - if order[1] >= 2: - gamma_s[0, 2] = aem2.gamma_singlet(n, nf, sx) - if order[0] == 3: - sx = np.append(sx, harmonics.S4(n)) - gamma_s[3, 0] = as3.gamma_QEDsinglet(n, nf, sx) + # if order[0] >= 1: + # gamma_s[1, 0] = as1.gamma_QEDsinglet(n, sx[0], nf) + # if order[1] >= 1: + # gamma_s[0, 1] = aem1.gamma_singlet(n, nf, sx) + # if order[0] >= 1 and order[1] >= 1: + # gamma_s[1, 1] = as1aem1.gamma_singlet(n, nf, sx) + # if order[0] >= 2: + # gamma_s[2, 0] = as2.gamma_QEDsinglet(n, nf, sx) + # if order[1] >= 2: + # gamma_s[0, 2] = aem2.gamma_singlet(n, nf, sx) + # if order[0] == 3: + # sx = np.append(sx, harmonics.S4(n)) + # gamma_s[3, 0] = as3.gamma_QEDsinglet(n, nf, sx) return gamma_s From d4e7deb9a0c04705a5210673304e3f99a0a24b99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 20 Jul 2022 12:29:34 +0200 Subject: [PATCH 065/312] Change dim of betaQCD in QEDsinglet.py and QEDvalence.py --- src/eko/kernels/QEDsinglet.py | 2 +- src/eko/kernels/QEDvalence.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/eko/kernels/QEDsinglet.py b/src/eko/kernels/QEDsinglet.py index 0be0265db..c0579c861 100644 --- a/src/eko/kernels/QEDsinglet.py +++ b/src/eko/kernels/QEDsinglet.py @@ -49,7 +49,7 @@ def eko_iterate(gamma_singlet, a1, a0, aem, nf, order, ev_op_iterations): # [0, 0, 0, 0], # ] # ) - betaQCD = np.zeros((4, 4), np.complex_) + betaQCD = np.zeros((4, 3), np.complex_) for i in range(4): betaQCD[i, 0] = beta.beta_qcd((i + 2, 0), nf) betaQCD[0, 1] = beta.beta_qcd((2, 1), nf) diff --git a/src/eko/kernels/QEDvalence.py b/src/eko/kernels/QEDvalence.py index 0a12ffdc1..1e1813105 100644 --- a/src/eko/kernels/QEDvalence.py +++ b/src/eko/kernels/QEDvalence.py @@ -47,7 +47,7 @@ def eko_iterate(gamma_valence, a1, a0, aem, nf, order, ev_op_iterations): # [0, 0, 0, 0], # ] # ) - betaQCD = np.zeros((4, 4), np.complex_) + betaQCD = np.zeros((4, 3), np.complex_) for i in range(4): betaQCD[i, 0] = beta.beta_qcd((i + 2, 0), nf) betaQCD[0, 1] = beta.beta_qcd((2, 1), nf) From 4085419678ef39835cfc64af31ea55982808196f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 20 Jul 2022 12:45:32 +0200 Subject: [PATCH 066/312] Rename Id -> id_ and Zero -> zero --- tests/eko/test_ad.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/eko/test_ad.py b/tests/eko/test_ad.py index e3b6704c4..72eb663e3 100644 --- a/tests/eko/test_ad.py +++ b/tests/eko/test_ad.py @@ -52,9 +52,9 @@ def test_exp_matrix(): assert_allclose(res, res2) diag = np.diag([1, 2, 3, 4]) assert_allclose(np.diag(np.exp([1, 2, 3, 4])), ad.exp_matrix(diag)[0]) - Id = np.identity(4, np.complex_) - Zero = np.zeros((4, 4), np.complex_) - assert_allclose(Id, ad.exp_matrix(Zero)[0]) + id_ = np.identity(4, np.complex_) + zero = np.zeros((4, 4), np.complex_) + assert_allclose(id_, ad.exp_matrix(zero)[0]) def test_eigensystem_gamma_singlet_projectors_EV(): From 1d6390d4e8662d6d6c9959d7ec2e4256cfa5b445 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 20 Jul 2022 14:23:21 +0200 Subject: [PATCH 067/312] Test dimensions of asn.gamma_QEDsinglet --- tests/eko/test_ad.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/tests/eko/test_ad.py b/tests/eko/test_ad.py index 72eb663e3..0a27cf19d 100644 --- a/tests/eko/test_ad.py +++ b/tests/eko/test_ad.py @@ -8,7 +8,7 @@ from eko import anomalous_dimensions as ad from eko import basis_rotation as br -from eko.anomalous_dimensions import as1, harmonics +from eko.anomalous_dimensions import as1, as2, as3, harmonics NF = 5 @@ -159,3 +159,17 @@ def test_gamma_ns_qed(): np.zeros((1, 3)), decimal=5, ) + + +def test_dim(): + nf = 3 + N = 2 + sx = harmonics.sx(N, max_weight=3 + 1) + gamma_singlet = ad.gamma_singlet_qed((3, 2), N, nf) + assert gamma_singlet.shape == (4, 3, 4, 4) + gamma_singlet_as1 = as1.gamma_QEDsinglet(N, sx[0], nf) + assert gamma_singlet_as1.shape == (4, 4) + gamma_singlet_as2 = as2.gamma_QEDsinglet(N, nf, sx) + assert gamma_singlet_as2.shape == (4, 4) + gamma_singlet_as3 = as3.gamma_QEDsinglet(N, nf, sx) + assert gamma_singlet_as3.shape == (4, 4) From ccc775b9c74530038ba897b8563eb2c42aee8eec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 20 Jul 2022 14:30:14 +0200 Subject: [PATCH 068/312] Make singlet matrices explicitly float --- src/eko/anomalous_dimensions/__init__.py | 26 ++++++++++++------------ src/eko/anomalous_dimensions/aem1.py | 8 ++++---- src/eko/anomalous_dimensions/aem2.py | 8 ++++---- src/eko/anomalous_dimensions/as1.py | 12 +++++------ src/eko/anomalous_dimensions/as2.py | 12 +++++------ src/eko/anomalous_dimensions/as3.py | 12 +++++------ 6 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index eaf406060..4948a823f 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -370,19 +370,19 @@ def gamma_singlet_qed(order, n, nf): max_weight = max(order) sx = harmonics.sx(n, max_weight=max_weight + 1) gamma_s = np.zeros((order[0] + 1, order[1] + 1, 4, 4), np.complex_) - # if order[0] >= 1: - # gamma_s[1, 0] = as1.gamma_QEDsinglet(n, sx[0], nf) - # if order[1] >= 1: - # gamma_s[0, 1] = aem1.gamma_singlet(n, nf, sx) - # if order[0] >= 1 and order[1] >= 1: - # gamma_s[1, 1] = as1aem1.gamma_singlet(n, nf, sx) - # if order[0] >= 2: - # gamma_s[2, 0] = as2.gamma_QEDsinglet(n, nf, sx) - # if order[1] >= 2: - # gamma_s[0, 2] = aem2.gamma_singlet(n, nf, sx) - # if order[0] == 3: - # sx = np.append(sx, harmonics.S4(n)) - # gamma_s[3, 0] = as3.gamma_QEDsinglet(n, nf, sx) + if order[0] >= 1: + gamma_s[1, 0] = as1.gamma_QEDsinglet(n, sx[0], nf) + if order[1] >= 1: + gamma_s[0, 1] = aem1.gamma_singlet(n, nf, sx) + if order[0] >= 1 and order[1] >= 1: + gamma_s[1, 1] = as1aem1.gamma_singlet(n, nf, sx) + if order[0] >= 2: + gamma_s[2, 0] = as2.gamma_QEDsinglet(n, nf, sx) + if order[1] >= 2: + gamma_s[0, 2] = aem2.gamma_singlet(n, nf, sx) + if order[0] == 3: + sx = np.append(sx, harmonics.S4(n)) + gamma_s[3, 0] = as3.gamma_QEDsinglet(n, nf, sx) return gamma_s diff --git a/src/eko/anomalous_dimensions/aem1.py b/src/eko/anomalous_dimensions/aem1.py index 3c5b7fa6e..96e46f387 100644 --- a/src/eko/anomalous_dimensions/aem1.py +++ b/src/eko/anomalous_dimensions/aem1.py @@ -107,21 +107,21 @@ def gamma_singlet(N, nf, sx): e2delta = constants.vde2m(nf) - constants.vue2m(nf) + constants.e2avg(nf) gamma_S_01 = np.array( [ - [0, 0, 0, 0], + [0.0, 0.0, 0.0, 0.0], [ - 0, + 0.0, gamma_phph(nf), e2avg * gamma_phq(N), constants.vue2m(nf) * gamma_phq(N), ], [ - 0, + 0.0, e2avg * gamma_qph(N, nf), e2avg * gamma_ns(N, sx), constants.vue2m(nf) * gamma_ns(N, sx), ], [ - 0, + 0.0, constants.vde2m(nf) * gamma_qph(N, nf), constants.vde2m(nf) * gamma_ns(N, sx), e2delta * gamma_ns(N, sx), diff --git a/src/eko/anomalous_dimensions/aem2.py b/src/eko/anomalous_dimensions/aem2.py index 7cdb03941..1e86bc919 100644 --- a/src/eko/anomalous_dimensions/aem2.py +++ b/src/eko/anomalous_dimensions/aem2.py @@ -337,9 +337,9 @@ def gamma_singlet(N, nf, sx): e2m = constants.eu2 - constants.ed2 gamma_S_02 = np.array( [ - [0, 0, 0, 0], + [0.0, 0.0, 0.0, 0.0], [ - 0, + 0.0, gamma_phph(N, nf), vu * constants.eu2 * gamma_phu(N, nf, sx) + vd * constants.ed2 * gamma_phd(N, nf, sx), @@ -350,7 +350,7 @@ def gamma_singlet(N, nf, sx): ), ], [ - 0, + 0.0, vu * constants.eu2 * gamma_uph(N, nf, sx) + vd * constants.ed2 * gamma_dph(N, nf, sx), vu * constants.eu2 * gamma_nspu(N, nf, sx) @@ -364,7 +364,7 @@ def gamma_singlet(N, nf, sx): ), ], [ - 0, + 0.0, vd * ( constants.eu2 * gamma_uph(N, nf, sx) diff --git a/src/eko/anomalous_dimensions/as1.py b/src/eko/anomalous_dimensions/as1.py index bd848a201..f9608977e 100644 --- a/src/eko/anomalous_dimensions/as1.py +++ b/src/eko/anomalous_dimensions/as1.py @@ -177,10 +177,10 @@ def gamma_QEDsinglet(N, s1, nf): gamma_qq = gamma_ns(N, s1) gamma_S = np.array( [ - [gamma_gg(N, s1, nf), 0, gamma_gq(N), 0], - [0, 0, 0, 0], - [gamma_qg(N, nf), 0, gamma_qq, 0], - [0, 0, 0, gamma_qq], + [gamma_gg(N, s1, nf), 0.0, gamma_gq(N), 0.0], + [0.0, 0.0, 0.0, 0.0], + [gamma_qg(N, nf), 0.0, gamma_qq, 0.0], + [0.0, 0.0, 0.0, gamma_qq], ], np.complex_, ) @@ -191,8 +191,8 @@ def gamma_QEDsinglet(N, s1, nf): def gamma_QEDvalence(N, s1): gamma_V = np.array( [ - [1, 0], - [0, 1], + [1.0, 0.0], + [0.0, 1.0], ], np.complex_, ) diff --git a/src/eko/anomalous_dimensions/as2.py b/src/eko/anomalous_dimensions/as2.py index 227e7e8f4..526509ecb 100644 --- a/src/eko/anomalous_dimensions/as2.py +++ b/src/eko/anomalous_dimensions/as2.py @@ -312,10 +312,10 @@ def gamma_QEDsinglet(N, nf, sx): gamma_qq = gamma_nsp(N, nf, sx) + gamma_ps(N, nf) gamma_S = np.array( [ - [gamma_gg(N, nf, sx), 0, gamma_gq(N, nf, sx), 0], - [0, 0, 0, 0], - [gamma_qg(N, nf, sx), 0, gamma_qq, 0], - [0, 0, 0, gamma_nsp(N, nf, sx)], + [gamma_gg(N, nf, sx), 0.0, gamma_gq(N, nf, sx), 0.0], + [0.0, 0.0, 0.0, 0.0], + [gamma_qg(N, nf, sx), 0.0, gamma_qq, 0.0], + [0.0, 0.0, 0.0, gamma_nsp(N, nf, sx)], ], np.complex_, ) @@ -326,8 +326,8 @@ def gamma_QEDsinglet(N, nf, sx): def gamma_QEDvalence(N, nf, sx): gamma_V = np.array( [ - [1, 0], - [0, 1], + [1.0, 0.0], + [0.0, 1.0], ], np.complex_, ) diff --git a/src/eko/anomalous_dimensions/as3.py b/src/eko/anomalous_dimensions/as3.py index 5b8c01463..75cbd1bb9 100644 --- a/src/eko/anomalous_dimensions/as3.py +++ b/src/eko/anomalous_dimensions/as3.py @@ -623,10 +623,10 @@ def gamma_QEDsinglet(N, nf, sx): gamma_qq = gamma_nsp(N, nf, sx) + gamma_ps(N, nf, sx) gamma_S = np.array( [ - [gamma_gg(N, nf, sx), 0, gamma_gq(N, nf, sx), 0], - [0, 0, 0, 0], - [gamma_qg(N, nf, sx), 0, gamma_qq, 0], - [0, 0, 0, gamma_nsp(N, nf, sx)], + [gamma_gg(N, nf, sx), 0.0, gamma_gq(N, nf, sx), 0.0], + [0.0, 0.0, 0.0, 0.0], + [gamma_qg(N, nf, sx), 0.0, gamma_qq, 0.0], + [0.0, 0.0, 0.0, gamma_nsp(N, nf, sx)], ], np.complex_, ) @@ -637,8 +637,8 @@ def gamma_QEDsinglet(N, nf, sx): def gamma_QEDvalence(N, nf, sx): gamma_V = np.array( [ - [gamma_nsv(N, nf, sx), 0], - [0, gamma_nsm(N, nf, sx)], + [gamma_nsv(N, nf, sx), 0.0], + [0.0, gamma_nsm(N, nf, sx)], ], np.complex_, ) From 5667097849d75503874a587d922f66e0cc5a53e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 20 Jul 2022 17:41:29 +0200 Subject: [PATCH 069/312] Make numba compile QEDsinglet sector --- src/eko/anomalous_dimensions/aem1.py | 8 ++++---- src/eko/anomalous_dimensions/aem2.py | 8 ++++---- src/eko/anomalous_dimensions/as1.py | 24 +++++++++++++++--------- src/eko/anomalous_dimensions/as2.py | 24 +++++++++++++++--------- src/eko/anomalous_dimensions/as3.py | 24 +++++++++++++++--------- 5 files changed, 53 insertions(+), 35 deletions(-) diff --git a/src/eko/anomalous_dimensions/aem1.py b/src/eko/anomalous_dimensions/aem1.py index 96e46f387..36e24b045 100644 --- a/src/eko/anomalous_dimensions/aem1.py +++ b/src/eko/anomalous_dimensions/aem1.py @@ -107,21 +107,21 @@ def gamma_singlet(N, nf, sx): e2delta = constants.vde2m(nf) - constants.vue2m(nf) + constants.e2avg(nf) gamma_S_01 = np.array( [ - [0.0, 0.0, 0.0, 0.0], + [0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j], [ - 0.0, + 0.0 + 0.0j, gamma_phph(nf), e2avg * gamma_phq(N), constants.vue2m(nf) * gamma_phq(N), ], [ - 0.0, + 0.0 + 0.0j, e2avg * gamma_qph(N, nf), e2avg * gamma_ns(N, sx), constants.vue2m(nf) * gamma_ns(N, sx), ], [ - 0.0, + 0.0 + 0.0j, constants.vde2m(nf) * gamma_qph(N, nf), constants.vde2m(nf) * gamma_ns(N, sx), e2delta * gamma_ns(N, sx), diff --git a/src/eko/anomalous_dimensions/aem2.py b/src/eko/anomalous_dimensions/aem2.py index 1e86bc919..ce129603f 100644 --- a/src/eko/anomalous_dimensions/aem2.py +++ b/src/eko/anomalous_dimensions/aem2.py @@ -337,9 +337,9 @@ def gamma_singlet(N, nf, sx): e2m = constants.eu2 - constants.ed2 gamma_S_02 = np.array( [ - [0.0, 0.0, 0.0, 0.0], + [0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j], [ - 0.0, + 0.0 + 0.0j, gamma_phph(N, nf), vu * constants.eu2 * gamma_phu(N, nf, sx) + vd * constants.ed2 * gamma_phd(N, nf, sx), @@ -350,7 +350,7 @@ def gamma_singlet(N, nf, sx): ), ], [ - 0.0, + 0.0 + 0.0j, vu * constants.eu2 * gamma_uph(N, nf, sx) + vd * constants.ed2 * gamma_dph(N, nf, sx), vu * constants.eu2 * gamma_nspu(N, nf, sx) @@ -364,7 +364,7 @@ def gamma_singlet(N, nf, sx): ), ], [ - 0.0, + 0.0 + 0.0j, vd * ( constants.eu2 * gamma_uph(N, nf, sx) diff --git a/src/eko/anomalous_dimensions/as1.py b/src/eko/anomalous_dimensions/as1.py index f9608977e..8b9a41a29 100644 --- a/src/eko/anomalous_dimensions/as1.py +++ b/src/eko/anomalous_dimensions/as1.py @@ -175,15 +175,21 @@ def gamma_QEDsinglet(N, s1, nf): gamma_gg : :math:`\gamma_{gg}^{(0)}` """ gamma_qq = gamma_ns(N, s1) - gamma_S = np.array( - [ - [gamma_gg(N, s1, nf), 0.0, gamma_gq(N), 0.0], - [0.0, 0.0, 0.0, 0.0], - [gamma_qg(N, nf), 0.0, gamma_qq, 0.0], - [0.0, 0.0, 0.0, gamma_qq], - ], - np.complex_, - ) + gamma_S = np.zeros((4, 4), np.complex_) + gamma_S[0, 0] = gamma_gg(N, s1, nf) + gamma_S[0, 2] = gamma_gq(N) + gamma_S[2, 0] = gamma_qg(N, nf) + gamma_S[2, 2] = gamma_qq + gamma_S[3, 3] = gamma_qq + # gamma_S = np.array( + # [ + # [gamma_gg(N, s1, nf), 0.0, gamma_gq(N), 0.0], + # [0.0, 0.0, 0.0, 0.0], + # [gamma_qg(N, nf), 0.0, gamma_qq, 0.0], + # [0.0, 0.0, 0.0, gamma_qq], + # ], + # np.complex_, + # ) return gamma_S diff --git a/src/eko/anomalous_dimensions/as2.py b/src/eko/anomalous_dimensions/as2.py index 526509ecb..eef893771 100644 --- a/src/eko/anomalous_dimensions/as2.py +++ b/src/eko/anomalous_dimensions/as2.py @@ -310,15 +310,21 @@ def gamma_QEDsinglet(N, nf, sx): gamma_gg : :math:`\gamma_{gg}^{(0)}` """ gamma_qq = gamma_nsp(N, nf, sx) + gamma_ps(N, nf) - gamma_S = np.array( - [ - [gamma_gg(N, nf, sx), 0.0, gamma_gq(N, nf, sx), 0.0], - [0.0, 0.0, 0.0, 0.0], - [gamma_qg(N, nf, sx), 0.0, gamma_qq, 0.0], - [0.0, 0.0, 0.0, gamma_nsp(N, nf, sx)], - ], - np.complex_, - ) + gamma_S = np.zeros((4, 4), np.complex_) + gamma_S[0, 0] = gamma_gg(N, nf, sx) + gamma_S[0, 2] = gamma_gq(N, nf, sx) + gamma_S[2, 0] = gamma_qg(N, nf, sx) + gamma_S[2, 2] = gamma_qq + gamma_S[3, 3] = gamma_nsp(N, nf, sx) + # gamma_S = np.array( + # [ + # [gamma_gg(N, nf, sx), 0.0, gamma_gq(N, nf, sx), 0.0], + # [0.0, 0.0, 0.0, 0.0], + # [gamma_qg(N, nf, sx), 0.0, gamma_qq, 0.0], + # [0.0, 0.0, 0.0, gamma_nsp(N, nf, sx)], + # ], + # np.complex_, + # ) return gamma_S diff --git a/src/eko/anomalous_dimensions/as3.py b/src/eko/anomalous_dimensions/as3.py index 75cbd1bb9..6d53e6aba 100644 --- a/src/eko/anomalous_dimensions/as3.py +++ b/src/eko/anomalous_dimensions/as3.py @@ -621,15 +621,21 @@ def gamma_QEDsinglet(N, nf, sx): gamma_gg : :math:`\gamma_{gg}^{(0)}` """ gamma_qq = gamma_nsp(N, nf, sx) + gamma_ps(N, nf, sx) - gamma_S = np.array( - [ - [gamma_gg(N, nf, sx), 0.0, gamma_gq(N, nf, sx), 0.0], - [0.0, 0.0, 0.0, 0.0], - [gamma_qg(N, nf, sx), 0.0, gamma_qq, 0.0], - [0.0, 0.0, 0.0, gamma_nsp(N, nf, sx)], - ], - np.complex_, - ) + gamma_S = np.zeros((4, 4), np.complex_) + gamma_S[0, 0] = gamma_gg(N, nf, sx) + gamma_S[0, 2] = gamma_gq(N, nf, sx) + gamma_S[2, 0] = gamma_qg(N, nf, sx) + gamma_S[2, 2] = gamma_qq + gamma_S[3, 3] = gamma_nsp(N, nf, sx) + # gamma_S = np.array( + # [ + # [gamma_gg(N, nf, sx), 0.0, gamma_gq(N, nf, sx), 0.0], + # [0.0, 0.0, 0.0, 0.0], + # [gamma_qg(N, nf, sx), 0.0, gamma_qq, 0.0], + # [0.0, 0.0, 0.0, gamma_nsp(N, nf, sx)], + # ], + # np.complex_, + # ) return gamma_S From 315560f4bf59e4aa9af8b1d6fce4516659118bc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 20 Jul 2022 19:19:11 +0200 Subject: [PATCH 070/312] Use gamma_variation also for QED --- src/eko/evolution_operator/__init__.py | 12 ++- src/eko/scale_variations/exponentiated.py | 110 +++++++++------------- 2 files changed, 51 insertions(+), 71 deletions(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index c9fb31ad7..2358a95bf 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -298,7 +298,9 @@ def quad_ker( # TODO check scale variations # scale var exponentiated is directly applied on gamma if sv_mode == sv.Modes.exponentiated: - gamma_s = sv.exponentiated.gamma_variation_qed(gamma_s, order, nf, L) + gamma_s[0][1:] = sv.exponentiated.gamma_variation( + gamma_s[0][1:], order, nf, L + ) ker = qed_s.dispatcher( order, method, @@ -320,7 +322,9 @@ def quad_ker( gamma_v = ad.gamma_valence_qed(order, ker_base.n, nf) # scale var exponentiated is directly applied on gamma if sv_mode == sv.Modes.exponentiated: - gamma_v = sv.exponentiated.gamma_variation_qed(gamma_v, order, nf, L) + gamma_v[0][1:] = sv.exponentiated.gamma_variation( + gamma_v[0][1:], order, nf, L + ) ker = qed_v.dispatcher( order, method, @@ -342,7 +346,9 @@ def quad_ker( gamma_ns = ad.gamma_ns_qed(order, mode0, ker_base.n, nf) # scale var exponentiated is directly applied on gamma if sv_mode == sv.Modes.exponentiated: - gamma_ns = sv.exponentiated.gamma_variation_qed(gamma_ns, order, nf, L) + gamma_ns[0][1:] = sv.exponentiated.gamma_variation( + gamma_ns[0][1:], order, nf, L + ) ker = qed_ns.dispatcher( order, method, diff --git a/src/eko/scale_variations/exponentiated.py b/src/eko/scale_variations/exponentiated.py index 40e1ff61f..58288526e 100644 --- a/src/eko/scale_variations/exponentiated.py +++ b/src/eko/scale_variations/exponentiated.py @@ -6,32 +6,6 @@ from .. import beta -# @nb.njit(cache=True) -# def gamma_variation(gamma, order, nf, L): -# """ -# Adjust the anomalous dimensions with the scale variations. - -# Parameters -# ---------- -# gamma : numpy.ndarray -# anomalous dimensions -# order : tuple(int,int) -# perturbation order -# nf : int -# number of active flavors -# L : float -# logarithmic ratio of factorization and renormalization scale - -# Returns -# ------- -# gamma : numpy.ndarray -# adjusted anomalous dimensions -# """ -# if order[1] == 0 : -# return gamma_variation_pure_qcd(gamma, order, nf, L) -# else : -# return gamma_variation_qed(gamma, order, nf, L) - @nb.njit(cache=True) def gamma_variation(gamma, order, nf, L): @@ -78,47 +52,47 @@ def gamma_variation(gamma, order, nf, L): return gamma -@nb.njit(cache=True) -def gamma_variation_qed(gamma, order, nf, L): - """ - Adjust the anomalous dimensions with the scale variations. +# @nb.njit(cache=True) +# def gamma_variation_qed(gamma, order, nf, L): +# """ +# Adjust the anomalous dimensions with the scale variations. - Parameters - ---------- - gamma : numpy.ndarray - anomalous dimensions - order : tuple(int,int) - perturbation order - nf : int - number of active flavors - L : float - logarithmic ratio of factorization and renormalization scale +# Parameters +# ---------- +# gamma : numpy.ndarray +# anomalous dimensions +# order : tuple(int,int) +# perturbation order +# nf : int +# number of active flavors +# L : float +# logarithmic ratio of factorization and renormalization scale - Returns - ------- - gamma : numpy.ndarray - adjusted anomalous dimensions - """ - # since we are modifying *in-place* be carefull, that the order matters! - # and indeed, we need to adjust the high elements first - beta0 = beta.beta_qcd((2, 0), nf) - beta1 = beta.beta_qcd((3, 0), nf) - if order[0] >= 4: - gamma[4, 0] -= ( - 3 * beta0 * L * gamma[3, 0] - + (2 * beta1 * L - 3 * beta0**2 * L**2) * gamma[2, 0] - + ( - beta.beta_qcd((4, 0), nf) * L - - 5 / 2 * beta1 * beta0 * L**2 - + beta0**3 * L**3 - ) - * gamma[1, 0] - ) - if order[0] >= 3: - gamma[3, 0] -= ( - 2 * beta0 * gamma[2, 0] * L - + (beta1 * L - beta0**2 * L**2) * gamma[1, 0] - ) - if order[0] >= 2: - gamma[2, 0] -= beta0 * gamma[1, 0] * L - return gamma +# Returns +# ------- +# gamma : numpy.ndarray +# adjusted anomalous dimensions +# """ +# # since we are modifying *in-place* be carefull, that the order matters! +# # and indeed, we need to adjust the high elements first +# beta0 = beta.beta_qcd((2, 0), nf) +# beta1 = beta.beta_qcd((3, 0), nf) +# if order[0] >= 4: +# gamma[4, 0] -= ( +# 3 * beta0 * L * gamma[3, 0] +# + (2 * beta1 * L - 3 * beta0**2 * L**2) * gamma[2, 0] +# + ( +# beta.beta_qcd((4, 0), nf) * L +# - 5 / 2 * beta1 * beta0 * L**2 +# + beta0**3 * L**3 +# ) +# * gamma[1, 0] +# ) +# if order[0] >= 3: +# gamma[3, 0] -= ( +# 2 * beta0 * gamma[2, 0] * L +# + (beta1 * L - beta0**2 * L**2) * gamma[1, 0] +# ) +# if order[0] >= 2: +# gamma[2, 0] -= beta0 * gamma[1, 0] * L +# return gamma From d469205fc1d670ebe3b40c2dc6bcae3c24f12d98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 21 Jul 2022 10:33:12 +0200 Subject: [PATCH 071/312] Test anomalous dim --- src/eko/evolution_operator/__init__.py | 2 +- tests/eko/test_ad.py | 53 +++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 2358a95bf..723352b0f 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -295,7 +295,6 @@ def quad_ker( # compute the actual evolution kernel for QEDxQCD if ker_base.is_QEDsinglet: gamma_s = ad.gamma_singlet_qed(order, ker_base.n, nf) - # TODO check scale variations # scale var exponentiated is directly applied on gamma if sv_mode == sv.Modes.exponentiated: gamma_s[0][1:] = sv.exponentiated.gamma_variation( @@ -313,6 +312,7 @@ def quad_ker( ev_op_max_order, ) # scale var expanded is applied on the kernel + # TODO : check expanded scale variations # if sv_mode == sv.Modes.expanded and not is_threshold: # ker = np.ascontiguousarray(ker) @ np.ascontiguousarray( # sv.expanded.singlet_variation(gamma_s, as1, order, nf, L) diff --git a/tests/eko/test_ad.py b/tests/eko/test_ad.py index 0a27cf19d..43714d454 100644 --- a/tests/eko/test_ad.py +++ b/tests/eko/test_ad.py @@ -3,6 +3,7 @@ import warnings import numpy as np +import pytest from numpy.testing import assert_allclose, assert_almost_equal, assert_raises from scipy.linalg import expm @@ -159,9 +160,31 @@ def test_gamma_ns_qed(): np.zeros((1, 3)), decimal=5, ) + # as2 + assert_almost_equal( + ad.gamma_ns_qed((2, 0), br.non_singlet_pids_map["ns-u"], 1, nf), + np.zeros((3, 1)), + decimal=5, + ) + assert_almost_equal( + ad.gamma_ns_qed((2, 0), br.non_singlet_pids_map["ns-d"], 1, nf), + np.zeros((3, 1)), + decimal=5, + ) + # as3 + assert_almost_equal( + ad.gamma_ns_qed((3, 0), br.non_singlet_pids_map["ns-u"], 1, nf), + np.zeros((4, 1)), + decimal=3, + ) + assert_almost_equal( + ad.gamma_ns_qed((3, 0), br.non_singlet_pids_map["ns-d"], 1, nf), + np.zeros((4, 1)), + decimal=3, + ) -def test_dim(): +def test_dim_singlet(): nf = 3 N = 2 sx = harmonics.sx(N, max_weight=3 + 1) @@ -173,3 +196,31 @@ def test_dim(): assert gamma_singlet_as2.shape == (4, 4) gamma_singlet_as3 = as3.gamma_QEDsinglet(N, nf, sx) assert gamma_singlet_as3.shape == (4, 4) + + +def test_dim_valence(): + nf = 3 + N = 2 + sx = harmonics.sx(N, max_weight=3 + 1) + gamma_valence = ad.gamma_valence_qed((3, 2), N, nf) + assert gamma_valence.shape == (4, 3, 2, 2) + gamma_valence_as1 = as1.gamma_QEDvalence(N, sx[0]) + assert gamma_valence_as1.shape == (2, 2) + gamma_valence_as2 = as2.gamma_QEDvalence(N, nf, sx) + assert gamma_valence_as2.shape == (2, 2) + gamma_valence_as3 = as3.gamma_QEDvalence(N, nf, sx) + assert gamma_valence_as3.shape == (2, 2) + + +def test_dim_nsp(): + nf = 3 + N = 2 + sx = harmonics.sx(N, max_weight=3 + 1) + gamma_nsup = ad.gamma_ns_qed((3, 2), 10102, N, nf) + assert gamma_nsup.shape == (4, 3) + gamma_nsdp = ad.gamma_ns_qed((3, 2), 10103, N, nf) + assert gamma_nsdp.shape == (4, 3) + with pytest.raises(NotImplementedError): + gamma_ns = ad.gamma_ns_qed((1, 1), 10106, N, nf) + with pytest.raises(NotImplementedError): + gamma_ns = ad.gamma_ns_qed((2, 0), 10106, N, nf) From bcc2e02fa8c4d0b0024cffa6972a89fc0ff78a0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 21 Jul 2022 10:43:33 +0200 Subject: [PATCH 072/312] Add test_zero_qed --- tests/eko/test_kernels_ei.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/eko/test_kernels_ei.py b/tests/eko/test_kernels_ei.py index 9d00069e7..227dbbbaf 100644 --- a/tests/eko/test_kernels_ei.py +++ b/tests/eko/test_kernels_ei.py @@ -24,6 +24,23 @@ def test_zero(): np.testing.assert_allclose(fnc(1, 1, nf), 0) +def test_zero_qed(): + """No evolution results in exp(0)""" + nf = 3 + for fnc in [ + ei.j00_qed, + ei.jm10, + ei.j11_exact_qed, + ei.j01_exact_qed, + ei.jm11_exact, + ei.j22_exact_qed, + ei.j12_exact_qed, + ei.j02_exact_qed, + ei.jm12_exact, + ]: + np.testing.assert_allclose(fnc(1, 1, 0.00058, nf), 0) + + def test_der_lo(): """LO derivative""" nf = 3 From 351a71679db03f63ac420e64742d7b4985fc7560 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 21 Jul 2022 11:14:02 +0200 Subject: [PATCH 073/312] Test inverse of hermitian matrix --- src/eko/anomalous_dimensions/__init__.py | 6 +++--- src/eko/evolution_operator/__init__.py | 1 + tests/eko/test_ad.py | 4 ++++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index 4948a823f..a48c289ed 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -109,10 +109,10 @@ def exp_matrix(gamma_S): # compute Matrix of coefficients w, v = np.linalg.eig(gamma_S) vT = np.transpose(v) - V = vT.dot(v) - C = np.linalg.inv(V) + matV = vT.dot(v) + matC = np.linalg.inv(matV) # compute projectors - tmp = C.dot(vT) + tmp = matC.dot(vT) dim = gamma_S.shape[0] e = np.zeros((dim, dim, dim), np.complex_) # TODO check if this loop can be entirely cast to numpy diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 723352b0f..977270804 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -297,6 +297,7 @@ def quad_ker( gamma_s = ad.gamma_singlet_qed(order, ker_base.n, nf) # scale var exponentiated is directly applied on gamma if sv_mode == sv.Modes.exponentiated: + # the aem terms do not get scale variations gamma_s[0][1:] = sv.exponentiated.gamma_variation( gamma_s[0][1:], order, nf, L ) diff --git a/tests/eko/test_ad.py b/tests/eko/test_ad.py index 43714d454..20941dc49 100644 --- a/tests/eko/test_ad.py +++ b/tests/eko/test_ad.py @@ -56,6 +56,10 @@ def test_exp_matrix(): id_ = np.identity(4, np.complex_) zero = np.zeros((4, 4), np.complex_) assert_allclose(id_, ad.exp_matrix(zero)[0]) + sigma2 = np.array([[0.0, -1.0j], [1.0j, 0.0]]) + exp = ad.exp_matrix(sigma2)[0] + exp_m = ad.exp_matrix(-sigma2)[0] + assert_almost_equal(np.identity(2, np.complex_), exp @ exp_m) def test_eigensystem_gamma_singlet_projectors_EV(): From b208bfc87965c048b5c78d1f21965316910dcd97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 21 Jul 2022 15:23:45 +0200 Subject: [PATCH 074/312] Collect stuff in initialize_op_members --- src/eko/evolution_operator/__init__.py | 55 +++++++++++++++++++------- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 977270804..58ccb009f 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -369,6 +369,33 @@ def quad_ker( return np.real(ker * integrand) +# @nb.njit(cache=True) +# def choose_matrix(order, mode0, ker_base, nf): +# if order[1] == 0: +# if ker_base.is_singlet: +# return ad.gamma_singlet(order, ker_base.n, nf) +# else: +# return ad.gamma_ns(order, mode0, ker_base.n, nf) +# else : +# if ker_base.is_QEDsinglet : +# return ad.gamma_singlet_qed(order, ker_base.n, nf) +# elif ker_base.is_QEDvalence: +# return ad.gamma_valence_qed(order, ker_base.n, nf) +# else: +# return ad.gamma_ns_qed(order, mode0, ker_base.n, nf) + +# @nb.njit(cache=True) +# def exponentiated_variation(order, gamma, nf, L): +# if order[1] == 0: +# gamma = sv.exponentiated.gamma_variation(gamma, order, nf, L) +# return gamma +# else : +# gamma[0][1:] = sv.exponentiated.gamma_variation( +# gamma[0][1:], order, nf, L +# ) +# return gamma + + class Operator(sv.ModeMixin): """Internal representation of a single EKO. @@ -488,7 +515,7 @@ def labels(self): else: labels.extend(br.singlet_labels) else: - # add +u and +d ad default + # add +u and +d as default labels.append(br.non_singlet_unified_labels[0]) labels.append(br.non_singlet_unified_labels[2]) # -u and -d become different starting from O(as1aem1) or O(aem2) @@ -544,22 +571,20 @@ def initialize_op_members(self): ) zero = OpMember(*[np.zeros((self.grid_size, self.grid_size))] * 2) if self.order[1] == 0: - for n in self.full_labels: - if n in self.labels: - # non-singlet evolution and diagonal op are identities - if n in br.non_singlet_labels or n[0] == n[1]: - self.op_members[n] = eye.copy() - else: - self.op_members[n] = zero.copy() + full_labels = self.full_labels + non_singlet_labels = br.non_singlet_labels + else: + full_labels = self.full_labels_qed + non_singlet_labels = br.non_singlet_unified_labels + for n in full_labels: + if n in self.labels: + # non-singlet evolution and diagonal op are identities + if n in non_singlet_labels or n[0] == n[1]: + self.op_members[n] = eye.copy() else: self.op_members[n] = zero.copy() - else: - for n in self.full_labels_qed: - if n in self.labels: - if n in br.non_singlet_unified_labels or n[0] == n[1]: - self.op_members[n] = eye.copy() - else: - self.op_members[n] = zero.copy() + else: + self.op_members[n] = zero.copy() def run_op_integration( self, From 429969a9b4bda031182c093cb98c74ce35084f55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 21 Jul 2022 15:38:11 +0200 Subject: [PATCH 075/312] Fix copy_ns_ops --- src/eko/evolution_operator/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 58ccb009f..24a4cf948 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -735,7 +735,7 @@ def copy_ns_ops(self): ].error = self.op_members[ (br.non_singlet_pids_map["ns-"], 0) ].error.copy() - elif self.order[1] == 1: + elif self.order[1] == 1 and self.order[0] == 0: self.op_members[ (br.non_singlet_pids_map["ns-u"], 0) ].value = self.op_members[(br.non_singlet_pids_map["ns+u"], 0)].value.copy() From b85243d1c7461d2514e27a3d3c33280e7610b4c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 21 Jul 2022 16:36:32 +0200 Subject: [PATCH 076/312] Fix bug: append -> extend --- src/eko/evolution_operator/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 24a4cf948..4abb6c6aa 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -522,8 +522,8 @@ def labels(self): if self.order[1] >= 2 or self.order[0] >= 1: labels.append(br.non_singlet_unified_labels[1]) labels.append(br.non_singlet_unified_labels[3]) - labels.append(br.valence_unified_labels) - labels.append(br.singlet_unified_labels) + labels.extend(br.valence_unified_labels) + labels.extend(br.singlet_unified_labels) return labels def quad_ker(self, label, logx, areas): From 4628c9af3b43185bf25fb45a8a38a7403e5a6690 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 22 Jul 2022 14:57:42 +0200 Subject: [PATCH 077/312] Collect stuff in to_flavor_basis_tensor --- src/eko/evolution_operator/grid.py | 6 +-- src/eko/evolution_operator/physical.py | 52 ++++++-------------------- 2 files changed, 13 insertions(+), 45 deletions(-) diff --git a/src/eko/evolution_operator/grid.py b/src/eko/evolution_operator/grid.py index 141035b0a..2a44f27e1 100644 --- a/src/eko/evolution_operator/grid.py +++ b/src/eko/evolution_operator/grid.py @@ -295,10 +295,8 @@ def generate(self, q2): flavors.rotate_matching(op.nf + 1), op.q2_to ) final_op = final_op @ rot @ matching @ phys_op - if self.config["order"][1] == 0: - values, errors = final_op.to_flavor_basis_tensor() - else: - values, errors = final_op.to_flavor_basis_tensor_qed() + is_qed = self.config["order"][1] > 0 + values, errors = final_op.to_flavor_basis_tensor(is_qed) return { "operators": values, "operator_errors": errors, diff --git a/src/eko/evolution_operator/physical.py b/src/eko/evolution_operator/physical.py index d72bb1d97..5ff97d0e4 100644 --- a/src/eko/evolution_operator/physical.py +++ b/src/eko/evolution_operator/physical.py @@ -147,7 +147,7 @@ def ad_to_uni_evol_map(cls, op_members, nf, q2_final, intrinsic_range): # map key to MemberName return cls.promote_names(m, q2_final) - def to_flavor_basis_tensor(self): + def to_flavor_basis_tensor(self, is_qed=False): """ Convert the computations into an rank 4 tensor over flavor operator space and momentum fraction operator space. @@ -164,46 +164,16 @@ def to_flavor_basis_tensor(self): value_tensor = np.zeros((len_pids, len_xgrid, len_pids, len_xgrid)) error_tensor = value_tensor.copy() for name, op in self.op_members.items(): - in_pids = flavors.pids_from_intrinsic_evol(name.input, nf_in, False) - out_pids = flavors.pids_from_intrinsic_evol(name.target, nf_out, True) - for out_idx, out_weight in enumerate(out_pids): - for in_idx, in_weight in enumerate(in_pids): - # keep the outer index to the left as we're multiplying from the right - value_tensor[ - out_idx, # output pid (position) - :, # output momentum fraction - in_idx, # input pid (position) - :, # input momentum fraction - ] += out_weight * (op.value * in_weight) - error_tensor[ - out_idx, # output pid (position) - :, # output momentum fraction - in_idx, # input pid (position) - :, # input momentum fraction - ] += out_weight * (op.error * in_weight) - return value_tensor, error_tensor - - def to_flavor_basis_tensor_qed(self): - """ - Convert the computations into an rank 4 tensor over flavor operator space and - momentum fraction operator space. - - Returns - ------- - tensor : numpy.ndarray - EKO - """ - nf_in, nf_out = flavors.get_range(self.op_members.keys()) - len_pids = len(br.flavor_basis_pids) - len_xgrid = list(self.op_members.values())[0].value.shape[0] - # dimension will be pids^2 * xgrid^2 - value_tensor = np.zeros((len_pids, len_xgrid, len_pids, len_xgrid)) - error_tensor = value_tensor.copy() - for name, op in self.op_members.items(): - in_pids = flavors.pids_from_intrinsic_unified_evol(name.input, nf_in, False) - out_pids = flavors.pids_from_intrinsic_unified_evol( - name.target, nf_out, True - ) + if not is_qed: + in_pids = flavors.pids_from_intrinsic_evol(name.input, nf_in, False) + out_pids = flavors.pids_from_intrinsic_evol(name.target, nf_out, True) + else: + in_pids = flavors.pids_from_intrinsic_unified_evol( + name.input, nf_in, False + ) + out_pids = flavors.pids_from_intrinsic_unified_evol( + name.target, nf_out, True + ) for out_idx, out_weight in enumerate(out_pids): for in_idx, in_weight in enumerate(in_pids): # keep the outer index to the left as we're multiplying from the right From cfa76b18d9cde9d8f9fd8aaa85f6d21d2f842db6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 22 Jul 2022 15:38:16 +0200 Subject: [PATCH 078/312] Factorize things inside ad_to_evol_map --- src/eko/evolution_operator/grid.py | 10 +- src/eko/evolution_operator/physical.py | 158 ++++++++++--------------- 2 files changed, 64 insertions(+), 104 deletions(-) diff --git a/src/eko/evolution_operator/grid.py b/src/eko/evolution_operator/grid.py index 2a44f27e1..b4a22f148 100644 --- a/src/eko/evolution_operator/grid.py +++ b/src/eko/evolution_operator/grid.py @@ -260,16 +260,14 @@ def generate(self, q2): is_downward = is_downward_path(path) if is_downward: intrinsic_range = [4, 5, 6] + is_qed = self.config["order"][1] > 0 final_op = physical.PhysicalOperator.ad_to_evol_map( - operator.op_members, - operator.nf, - operator.q2_to, - intrinsic_range, + operator.op_members, operator.nf, operator.q2_to, intrinsic_range, is_qed ) # and multiply the lower ones from the right for op in reversed(list(thr_ops)): phys_op = physical.PhysicalOperator.ad_to_evol_map( - op.op_members, op.nf, op.q2_to, intrinsic_range + op.op_members, op.nf, op.q2_to, intrinsic_range, is_qed ) # join with the basis rotation, since matching requires c+ (or likewise) @@ -295,7 +293,7 @@ def generate(self, q2): flavors.rotate_matching(op.nf + 1), op.q2_to ) final_op = final_op @ rot @ matching @ phys_op - is_qed = self.config["order"][1] > 0 + # is_qed = self.config["order"][1] > 0 values, errors = final_op.to_flavor_basis_tensor(is_qed) return { "operators": values, diff --git a/src/eko/evolution_operator/physical.py b/src/eko/evolution_operator/physical.py index 5ff97d0e4..ad48d45bf 100644 --- a/src/eko/evolution_operator/physical.py +++ b/src/eko/evolution_operator/physical.py @@ -25,7 +25,7 @@ class PhysicalOperator(member.OperatorBase): """ @classmethod - def ad_to_evol_map(cls, op_members, nf, q2_final, intrinsic_range): + def ad_to_evol_map(cls, op_members, nf, q2_final, intrinsic_range, is_qed=False): """ Obtain map between the 3-dimensional anomalous dimension basis and the 4-dimensional evolution basis. @@ -47,103 +47,65 @@ def ad_to_evol_map(cls, op_members, nf, q2_final, intrinsic_range): map """ # constant elements - m = { - "S.S": op_members[(100, 100)], - "S.g": op_members[(100, 21)], - "g.g": op_members[(21, 21)], - "g.S": op_members[(21, 100)], - "V.V": op_members[(br.non_singlet_pids_map["nsV"], 0)], - } - # add elements which are already active - for f in range(2, nf + 1): - n = f**2 - 1 - m[f"V{n}.V{n}"] = op_members[(br.non_singlet_pids_map["ns-"], 0)] - m[f"T{n}.T{n}"] = op_members[(br.non_singlet_pids_map["ns+"], 0)] - # deal with intrinsic heavy quark PDFs - if intrinsic_range is not None: - hqfl = "cbt" - op_id = member.OpMember.id_like( - op_members[(br.non_singlet_pids_map["nsV"], 0)] - ) - for intr_fl in intrinsic_range: - if intr_fl <= nf: # light quarks are not intrinsic - continue - hq = hqfl[intr_fl - 4] # find name - # intrinsic means no evolution, i.e. they are evolving with the identity - m[f"{hq}+.{hq}+"] = op_id.copy() - m[f"{hq}-.{hq}-"] = op_id.copy() - # map key to MemberName - return cls.promote_names(m, q2_final) - - @classmethod - def ad_to_uni_evol_map(cls, op_members, nf, q2_final, intrinsic_range): - """ - Obtain map between the 3-dimensional anomalous dimension basis and the - 4-dimensional evolution basis. - - .. todo:: in VFNS sometimes IC is irrelevant if nf>=4 - - Parameters - ---------- - op_members : dict - operator members in anomalous dimension basis - nf : int - number of active light flavors - intrinsic_range : sequence - intrinsic heavy flavors - - Returns - ------- - m : dict - map - """ - # constant elements - m = { - "g.g": op_members[(21, 21)], - "g.ph": op_members[(21, 22)], - "g.S": op_members[(21, 100)], - "g.Sdelta": op_members[(21, 101)], - "ph.g": op_members[(22, 21)], - "ph.ph": op_members[(22, 22)], - "ph.S": op_members[(22, 100)], - "ph.Sdelta": op_members[(22, 101)], - "S.g": op_members[(100, 21)], - "S.ph": op_members[(100, 22)], - "S.S": op_members[(100, 100)], - "S.Sdelta": op_members[(100, 101)], - "V.V": op_members[(10200, 10200)], - "V.Vdelta": op_members[(10200, 10204)], - "Vdelta.V": op_members[(10204, 10200)], - "Vdelta.Vdelta": op_members[(10204, 10204)], - } - # add elements which are already active - if nf >= 3: - m["Td3.Td3"] = op_members[(br.non_singlet_pids_map["ns+d"], 0)] - m["Vd3.Vd3"] = op_members[(br.non_singlet_pids_map["ns-d"], 0)] - if nf >= 4: - m["Tu3.Tu3"] = op_members[(br.non_singlet_pids_map["ns+u"], 0)] - m["Vu3.Vu3"] = op_members[(br.non_singlet_pids_map["ns-u"], 0)] - if nf >= 5: - m["Td8.Td8"] = op_members[(br.non_singlet_pids_map["ns+d"], 0)] - m["Vd8.Vd8"] = op_members[(br.non_singlet_pids_map["ns-d"], 0)] - if nf >= 6: - m["Tu8.Tu8"] = op_members[(br.non_singlet_pids_map["ns+u"], 0)] - m["Vu8.Vu8"] = op_members[(br.non_singlet_pids_map["ns-u"], 0)] - # deal with intrinsic heavy quark PDFs - if intrinsic_range is not None: - hqfl = "cbt" - op_id = member.OpMember.id_like( - op_members[ - (br.non_singlet_pids_map["nsV"], 0) - ] # TODO : Probably it is wrong - ) - for intr_fl in intrinsic_range: - if intr_fl <= nf: # light quarks are not intrinsic - continue - hq = hqfl[intr_fl - 4] # find name - # intrinsic means no evolution, i.e. they are evolving with the identity - m[f"{hq}+.{hq}+"] = op_id.copy() - m[f"{hq}-.{hq}-"] = op_id.copy() + if not is_qed: + m = { + "S.S": op_members[(100, 100)], + "S.g": op_members[(100, 21)], + "g.g": op_members[(21, 21)], + "g.S": op_members[(21, 100)], + "V.V": op_members[(br.non_singlet_pids_map["nsV"], 0)], + } + # add elements which are already active + for f in range(2, nf + 1): + n = f**2 - 1 + m[f"V{n}.V{n}"] = op_members[(br.non_singlet_pids_map["ns-"], 0)] + m[f"T{n}.T{n}"] = op_members[(br.non_singlet_pids_map["ns+"], 0)] + # deal with intrinsic heavy quark PDFs + if intrinsic_range is not None: + hqfl = "cbt" + op_id = member.OpMember.id_like( + op_members[(br.non_singlet_pids_map["nsV"], 0)] + ) + for intr_fl in intrinsic_range: + if intr_fl <= nf: # light quarks are not intrinsic + continue + hq = hqfl[intr_fl - 4] # find name + # intrinsic means no evolution, i.e. they are evolving with the identity + m[f"{hq}+.{hq}+"] = op_id.copy() + m[f"{hq}-.{hq}-"] = op_id.copy() + else: + m = { + "g.g": op_members[(21, 21)], + "g.ph": op_members[(21, 22)], + "g.S": op_members[(21, 100)], + "g.Sdelta": op_members[(21, 101)], + "ph.g": op_members[(22, 21)], + "ph.ph": op_members[(22, 22)], + "ph.S": op_members[(22, 100)], + "ph.Sdelta": op_members[(22, 101)], + "S.g": op_members[(100, 21)], + "S.ph": op_members[(100, 22)], + "S.S": op_members[(100, 100)], + "S.Sdelta": op_members[(100, 101)], + "V.V": op_members[(10200, 10200)], + "V.Vdelta": op_members[(10200, 10204)], + "Vdelta.V": op_members[(10204, 10200)], + "Vdelta.Vdelta": op_members[(10204, 10204)], + } + # add elements which are already active + if nf >= 3: + m["Td3.Td3"] = op_members[(br.non_singlet_pids_map["ns+d"], 0)] + m["Vd3.Vd3"] = op_members[(br.non_singlet_pids_map["ns-d"], 0)] + if nf >= 4: + m["Tu3.Tu3"] = op_members[(br.non_singlet_pids_map["ns+u"], 0)] + m["Vu3.Vu3"] = op_members[(br.non_singlet_pids_map["ns-u"], 0)] + if nf >= 5: + m["Td8.Td8"] = op_members[(br.non_singlet_pids_map["ns+d"], 0)] + m["Vd8.Vd8"] = op_members[(br.non_singlet_pids_map["ns-d"], 0)] + if nf >= 6: + m["Tu8.Tu8"] = op_members[(br.non_singlet_pids_map["ns+u"], 0)] + m["Vu8.Vu8"] = op_members[(br.non_singlet_pids_map["ns-u"], 0)] + # TODO : check intrinsic quarks for QED # map key to MemberName return cls.promote_names(m, q2_final) From 25722bacac6b5bd4badb5183565f31d7cb821a93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 22 Jul 2022 15:41:45 +0200 Subject: [PATCH 079/312] Deal with intrinsic quarks also for QED --- src/eko/evolution_operator/physical.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/eko/evolution_operator/physical.py b/src/eko/evolution_operator/physical.py index ad48d45bf..e175bdb3a 100644 --- a/src/eko/evolution_operator/physical.py +++ b/src/eko/evolution_operator/physical.py @@ -105,7 +105,19 @@ def ad_to_evol_map(cls, op_members, nf, q2_final, intrinsic_range, is_qed=False) if nf >= 6: m["Tu8.Tu8"] = op_members[(br.non_singlet_pids_map["ns+u"], 0)] m["Vu8.Vu8"] = op_members[(br.non_singlet_pids_map["ns-u"], 0)] - # TODO : check intrinsic quarks for QED + # deal with intrinsic heavy quark PDFs + if intrinsic_range is not None: + hqfl = "cbt" + op_id = member.OpMember.id_like( + op_members[(br.non_singlet_pids_map["nsV"], 0)] + ) + for intr_fl in intrinsic_range: + if intr_fl <= nf: # light quarks are not intrinsic + continue + hq = hqfl[intr_fl - 4] # find name + # intrinsic means no evolution, i.e. they are evolving with the identity + m[f"{hq}+.{hq}+"] = op_id.copy() + m[f"{hq}-.{hq}-"] = op_id.copy() # map key to MemberName return cls.promote_names(m, q2_final) From b0f8a8fcacd12fa93e5fcb10721fa98357d2f3d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Sat, 23 Jul 2022 13:49:18 +0200 Subject: [PATCH 080/312] Fix ad_to_evol_map --- src/eko/evolution_operator/physical.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/eko/evolution_operator/physical.py b/src/eko/evolution_operator/physical.py index e175bdb3a..1fb7a9abe 100644 --- a/src/eko/evolution_operator/physical.py +++ b/src/eko/evolution_operator/physical.py @@ -108,9 +108,14 @@ def ad_to_evol_map(cls, op_members, nf, q2_final, intrinsic_range, is_qed=False) # deal with intrinsic heavy quark PDFs if intrinsic_range is not None: hqfl = "cbt" - op_id = member.OpMember.id_like( - op_members[(br.non_singlet_pids_map["nsV"], 0)] - ) + if not is_qed: + op_id = member.OpMember.id_like( + op_members[(br.non_singlet_pids_map["nsV"], 0)] + ) + else: + op_id = member.OpMember.id_like( + op_members[br.valence_unified_labels[0]] + ) for intr_fl in intrinsic_range: if intr_fl <= nf: # light quarks are not intrinsic continue From ac5e0e746221a5f21315f84fae7cc6db1e224fa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 25 Jul 2022 11:42:50 +0200 Subject: [PATCH 081/312] Change (br.non_singlet_pids_map[nsV], 0) -> (21,21) --- src/eko/evolution_operator/physical.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/eko/evolution_operator/physical.py b/src/eko/evolution_operator/physical.py index 1fb7a9abe..38cf7c406 100644 --- a/src/eko/evolution_operator/physical.py +++ b/src/eko/evolution_operator/physical.py @@ -109,9 +109,7 @@ def ad_to_evol_map(cls, op_members, nf, q2_final, intrinsic_range, is_qed=False) if intrinsic_range is not None: hqfl = "cbt" if not is_qed: - op_id = member.OpMember.id_like( - op_members[(br.non_singlet_pids_map["nsV"], 0)] - ) + op_id = member.OpMember.id_like(op_members[(21, 21)]) else: op_id = member.OpMember.id_like( op_members[br.valence_unified_labels[0]] From 22506f4d0e67214fb3ead38e3cc3ba47aa4a214f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 25 Jul 2022 12:06:09 +0200 Subject: [PATCH 082/312] Factor out const elements in ad_to_evol_map --- src/eko/evolution_operator/physical.py | 48 ++++++++++++-------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/src/eko/evolution_operator/physical.py b/src/eko/evolution_operator/physical.py index 38cf7c406..4ab6e6168 100644 --- a/src/eko/evolution_operator/physical.py +++ b/src/eko/evolution_operator/physical.py @@ -47,14 +47,14 @@ def ad_to_evol_map(cls, op_members, nf, q2_final, intrinsic_range, is_qed=False) map """ # constant elements + m = { + "S.S": op_members[(100, 100)], + "S.g": op_members[(100, 21)], + "g.g": op_members[(21, 21)], + "g.S": op_members[(21, 100)], + } if not is_qed: - m = { - "S.S": op_members[(100, 100)], - "S.g": op_members[(100, 21)], - "g.g": op_members[(21, 21)], - "g.S": op_members[(21, 100)], - "V.V": op_members[(br.non_singlet_pids_map["nsV"], 0)], - } + m.update({"V.V": op_members[(br.non_singlet_pids_map["nsV"], 0)]}) # add elements which are already active for f in range(2, nf + 1): n = f**2 - 1 @@ -74,24 +74,22 @@ def ad_to_evol_map(cls, op_members, nf, q2_final, intrinsic_range, is_qed=False) m[f"{hq}+.{hq}+"] = op_id.copy() m[f"{hq}-.{hq}-"] = op_id.copy() else: - m = { - "g.g": op_members[(21, 21)], - "g.ph": op_members[(21, 22)], - "g.S": op_members[(21, 100)], - "g.Sdelta": op_members[(21, 101)], - "ph.g": op_members[(22, 21)], - "ph.ph": op_members[(22, 22)], - "ph.S": op_members[(22, 100)], - "ph.Sdelta": op_members[(22, 101)], - "S.g": op_members[(100, 21)], - "S.ph": op_members[(100, 22)], - "S.S": op_members[(100, 100)], - "S.Sdelta": op_members[(100, 101)], - "V.V": op_members[(10200, 10200)], - "V.Vdelta": op_members[(10200, 10204)], - "Vdelta.V": op_members[(10204, 10200)], - "Vdelta.Vdelta": op_members[(10204, 10204)], - } + m.update( + { + "g.ph": op_members[(21, 22)], + "g.Sdelta": op_members[(21, 101)], + "ph.g": op_members[(22, 21)], + "ph.ph": op_members[(22, 22)], + "ph.S": op_members[(22, 100)], + "ph.Sdelta": op_members[(22, 101)], + "S.ph": op_members[(100, 22)], + "S.Sdelta": op_members[(100, 101)], + "V.V": op_members[(10200, 10200)], + "V.Vdelta": op_members[(10200, 10204)], + "Vdelta.V": op_members[(10204, 10200)], + "Vdelta.Vdelta": op_members[(10204, 10204)], + } + ) # add elements which are already active if nf >= 3: m["Td3.Td3"] = op_members[(br.non_singlet_pids_map["ns+d"], 0)] From 8d53a98f7547fe9ad527fc11e968a7081a3d949e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 25 Jul 2022 12:14:10 +0200 Subject: [PATCH 083/312] Remove if condition --- src/eko/evolution_operator/physical.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/eko/evolution_operator/physical.py b/src/eko/evolution_operator/physical.py index 4ab6e6168..13a204c53 100644 --- a/src/eko/evolution_operator/physical.py +++ b/src/eko/evolution_operator/physical.py @@ -106,12 +106,7 @@ def ad_to_evol_map(cls, op_members, nf, q2_final, intrinsic_range, is_qed=False) # deal with intrinsic heavy quark PDFs if intrinsic_range is not None: hqfl = "cbt" - if not is_qed: - op_id = member.OpMember.id_like(op_members[(21, 21)]) - else: - op_id = member.OpMember.id_like( - op_members[br.valence_unified_labels[0]] - ) + op_id = member.OpMember.id_like(op_members[(21, 21)]) for intr_fl in intrinsic_range: if intr_fl <= nf: # light quarks are not intrinsic continue From edcf2a4a5773cc53c2f231fd43077eb0f1c0a34b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 26 Jul 2022 21:29:27 +0200 Subject: [PATCH 084/312] Add valence_unified_labels to full_unified_labels --- src/eko/basis_rotation.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/eko/basis_rotation.py b/src/eko/basis_rotation.py index a768d3bed..97271e722 100644 --- a/src/eko/basis_rotation.py +++ b/src/eko/basis_rotation.py @@ -133,7 +133,11 @@ (non_singlet_pids_map["ns-u"], 0), ) full_labels = (*singlet_labels, *non_singlet_labels) -full_unified_labels = (*singlet_unified_labels, *non_singlet_unified_labels) +full_unified_labels = ( + *singlet_unified_labels, + *valence_unified_labels, + *non_singlet_unified_labels, +) anomalous_dimensions_basis = full_labels r""" From 2a0051100491361695b978389b9e2b2ef8220139 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 27 Jul 2022 12:58:02 +0200 Subject: [PATCH 085/312] Implement unified_evol_basis_pids --- src/eko/basis_rotation.py | 51 +++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/src/eko/basis_rotation.py b/src/eko/basis_rotation.py index 97271e722..1bdc2e53d 100644 --- a/src/eko/basis_rotation.py +++ b/src/eko/basis_rotation.py @@ -6,6 +6,8 @@ import numpy as np +from . import constants + flavor_basis_pids = tuple([22] + list(range(-6, -1 + 1)) + [21] + list(range(1, 6 + 1))) r""" Sorted elements in Flavor Basis as |pid|. @@ -85,6 +87,14 @@ ) """|pid| representation of :data:`evol_basis`.""" +unified_evol_basis_pids = tuple( + [21, 22, 100, 101, 200, 201] + + [103 + 2, 203 + 2] + + [103 + 1, 203 + 1] + + [108 + 2, 208 + 2] + + [108 + 1, 208 + 1] +) + non_singlet_pids_map = { "ns-": 10201, "ns+": 10101, @@ -170,24 +180,29 @@ """ # Tranformation from physical basis to QCDxQED evolution basis -rotate_flavor_to_unified_evolution = np.array( - [ - [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], - [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1], - [0, 1, -1, 1, -1, 1, -1, 0, -1, 1, -1, 1, -1, 1], - [0, -1, -1, -1, -1, -1, -1, 0, 1, 1, 1, 1, 1, 1], - [0, -1, 1, -1, 1, -1, 1, 0, -1, 1, -1, 1, -1, 1], - [0, 0, 0, 0, -1, 0, 1, 0, 1, 0, -1, 0, 0, 0], - [0, 0, 0, 0, 1, 0, -1, 0, 1, 0, -1, 0, 0, 0], - [0, 0, 0, -1, 0, 1, 0, 0, 0, 1, 0, -1, 0, 0], - [0, 0, 0, 1, 0, -1, 0, 0, 0, 1, 0, -1, 0, 0], - [0, 0, -2, 0, 1, 0, 1, 0, 1, 0, 1, 0, -2, 0], - [0, 0, 2, 0, -1, 0, -1, 0, 1, 0, 1, 0, -2, 0], - [0, -2, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, -2], - [0, 2, 0, -1, 0, -1, 0, 0, 0, 1, 0, 1, 0, -2], - ] -) +def rotate_flavor_to_unified_evolution(nf): + nu = constants.uplike_flavors(nf) + nd = nf - nu + rn = nd / nu + return np.array( + [ + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], + [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1], + [0, rn, -1, rn, -1, rn, -1, 0, -1, rn, -1, rn, -1, rn], + [0, -1, -1, -1, -1, -1, -1, 0, 1, 1, 1, 1, 1, 1], + [0, -rn, 1, -rn, 1, -rn, 1, 0, -1, rn, -1, rn, -1, rn], + [0, 0, 0, 0, -1, 0, 1, 0, 1, 0, -1, 0, 0, 0], + [0, 0, 0, 0, 1, 0, -1, 0, 1, 0, -1, 0, 0, 0], + [0, 0, 0, -1, 0, 1, 0, 0, 0, 1, 0, -1, 0, 0], + [0, 0, 0, 1, 0, -1, 0, 0, 0, 1, 0, -1, 0, 0], + [0, 0, -2, 0, 1, 0, 1, 0, 1, 0, 1, 0, -2, 0], + [0, 0, 2, 0, -1, 0, -1, 0, 1, 0, 1, 0, -2, 0], + [0, -2, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, -2], + [0, 2, 0, -1, 0, -1, 0, 0, 0, 1, 0, 1, 0, -2], + ] + ) + map_ad_to_evolution = { (100, 100): ["S.S"], From 8f620916e7faa8a56884fcef99a68e3e4bb9d475 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 27 Jul 2022 15:01:39 +0200 Subject: [PATCH 086/312] Add QED cases in get_range --- src/eko/evolution_operator/flavors.py | 22 +++++++++++++++++----- src/eko/evolution_operator/physical.py | 2 +- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/eko/evolution_operator/flavors.py b/src/eko/evolution_operator/flavors.py index ed62fe0ab..e8de28637 100644 --- a/src/eko/evolution_operator/flavors.py +++ b/src/eko/evolution_operator/flavors.py @@ -56,7 +56,7 @@ def pids_from_intrinsic_evol(label, nf, normalize): return weights -def get_range(evol_labels): +def get_range(evol_labels, is_qed=False): """ Determine the number of light and heavy flavors participating in the input and output. @@ -73,15 +73,27 @@ def get_range(evol_labels): nf_in = 3 nf_out = 3 - def update(label): + def update(label, is_qed=False): nf = 3 if label[0] == "T": - nf = round(np.sqrt(int(label[1:]) + 1)) + if not is_qed: + nf = round(np.sqrt(int(label[1:]) + 1)) + else: + if label[1:] == "d3": + nf = 3 + elif label[1:] == "u3": + nf = 4 + elif label[1:] == "d8": + nf = 5 + elif label[1:] == "u8": + nf = 6 + else: + raise ValueError(f"{label[1:]} is not possible") return nf for op in evol_labels: - nf_in = max(update(op.input), nf_in) - nf_out = max(update(op.target), nf_out) + nf_in = max(update(op.input, is_qed), nf_in) + nf_out = max(update(op.target, is_qed), nf_out) return nf_in, nf_out diff --git a/src/eko/evolution_operator/physical.py b/src/eko/evolution_operator/physical.py index 13a204c53..4c08ad082 100644 --- a/src/eko/evolution_operator/physical.py +++ b/src/eko/evolution_operator/physical.py @@ -127,7 +127,7 @@ def to_flavor_basis_tensor(self, is_qed=False): tensor : numpy.ndarray EKO """ - nf_in, nf_out = flavors.get_range(self.op_members.keys()) + nf_in, nf_out = flavors.get_range(self.op_members.keys(), is_qed) len_pids = len(br.flavor_basis_pids) len_xgrid = list(self.op_members.values())[0].value.shape[0] # dimension will be pids^2 * xgrid^2 From f2ab66a67284989d3d04c89fbb5e4a4be1440853 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 27 Jul 2022 19:32:06 +0200 Subject: [PATCH 087/312] Include QED in ad_projector --- src/eko/basis_rotation.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/eko/basis_rotation.py b/src/eko/basis_rotation.py index 1bdc2e53d..95ab8e56a 100644 --- a/src/eko/basis_rotation.py +++ b/src/eko/basis_rotation.py @@ -229,7 +229,7 @@ def rotate_flavor_to_unified_evolution(nf): Map anomalous dimension sectors' names to their members """ -map_ad_to_intrinsic_evolution = { +map_ad_to_unified_evolution = { (21, 21): ["g.g"], (21, 22): ["g.ph"], (21, 100): ["g.S"], @@ -269,7 +269,7 @@ def rotate_flavor_to_unified_evolution(nf): } -def ad_projector(ad_lab, nf): +def ad_projector(ad_lab, nf, is_qed=False): """ Build a projector (as a numpy array) for the given anomalous dimension sector. @@ -286,8 +286,16 @@ def ad_projector(ad_lab, nf): proj : np.ndarray projector over the specified sector """ - proj = np.zeros_like(rotate_flavor_to_evolution, dtype=float) - l = map_ad_to_evolution[ad_lab] + if not is_qed: + proj = np.zeros_like(rotate_flavor_to_evolution, dtype=float) + l = map_ad_to_evolution[ad_lab] + basis = evol_basis + rotate = rotate_flavor_to_evolution + else: + proj = np.zeros_like(rotate_flavor_to_unified_evolution, dtype=float) + l = map_ad_to_unified_evolution[ad_lab] + basis = unified_evol_basis + rotate = rotate_flavor_to_unified_evolution(nf) # restrict the evolution basis to light flavors # NOTE: the cut is only needed for "NS_p" and "NS_m", but the other lists # are 1-long so they are unaffected @@ -295,10 +303,10 @@ def ad_projector(ad_lab, nf): for el in l: out_name, in_name = el.split(".") - out_idx = evol_basis.index(out_name) - in_idx = evol_basis.index(in_name) - out = rotate_flavor_to_evolution[out_idx].copy() - in_ = rotate_flavor_to_evolution[in_idx].copy() + out_idx = basis.index(out_name) + in_idx = basis.index(in_name) + out = rotate[out_idx].copy() + in_ = rotate[in_idx].copy() out[: 1 + (6 - nf)] = out[len(out) - (6 - nf) :] = 0 in_[: 1 + (6 - nf)] = in_[len(in_) - (6 - nf) :] = 0 proj += (out[:, np.newaxis] * in_) / (out @ out) @@ -306,7 +314,7 @@ def ad_projector(ad_lab, nf): return proj -def ad_projectors(nf): +def ad_projectors(nf, is_qed=False): """ Build projectors tensor (as a numpy array), collecting all the individual sector projectors. @@ -323,7 +331,7 @@ def ad_projectors(nf): """ projs = [] for ad in anomalous_dimensions_basis: - projs.append(ad_projector(ad, nf)) + projs.append(ad_projector(ad, nf, is_qed)) return np.array(projs) From f70f6d6f869a6870dd0b660aa5f8725e63a5df9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 27 Jul 2022 21:24:53 +0200 Subject: [PATCH 088/312] Add comment --- src/eko/basis_rotation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eko/basis_rotation.py b/src/eko/basis_rotation.py index 95ab8e56a..ec005289d 100644 --- a/src/eko/basis_rotation.py +++ b/src/eko/basis_rotation.py @@ -290,7 +290,7 @@ def ad_projector(ad_lab, nf, is_qed=False): proj = np.zeros_like(rotate_flavor_to_evolution, dtype=float) l = map_ad_to_evolution[ad_lab] basis = evol_basis - rotate = rotate_flavor_to_evolution + rotate = rotate_flavor_to_evolution.copy() # Maybe .copy() is useless else: proj = np.zeros_like(rotate_flavor_to_unified_evolution, dtype=float) l = map_ad_to_unified_evolution[ad_lab] From 6a084a4f03cb2e8f1da77e9c0d4461056a3cca20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 28 Jul 2022 17:13:56 +0200 Subject: [PATCH 089/312] Init apfel_bench_qed.py --- benchmarks/apfel_bench_qed.py | 79 +++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 benchmarks/apfel_bench_qed.py diff --git a/benchmarks/apfel_bench_qed.py b/benchmarks/apfel_bench_qed.py new file mode 100644 index 000000000..b5a66c54a --- /dev/null +++ b/benchmarks/apfel_bench_qed.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +""" + Benchmark EKO to Apfel +""" +import numpy as np +from banana import register +from banana.data import cartesian_product + +from ekomark.benchmark.runner import Runner +from ekomark.data import operators + +register(__file__) + + +def tolist(input_dict): + output_dict = input_dict.copy() + for key, item in output_dict.items(): + if not isinstance(item, list): + output_dict[key] = [item] + return output_dict + + +class ApfelBenchmark(Runner): + + """ + Globally set the external program to Apfel + """ + + external = "apfel" + + # Rotate to evolution basis + rotate_to_evolution_basis = False + + @staticmethod + def skip_pdfs(_theory): + # pdf to skip + return [-6, 6, "T35", "V35"] + + +class BenchmarkFFNS(ApfelBenchmark): + """Benckmark FFNS""" + + ffns_theory = { + "FNS": "FFNS", + "ModEv": [ + "EXA", + # "EXP", + # "TRN", + ], + "NfFF": 5, + "kcThr": 0.0, + "kbThr": 0.0, + "ktThr": np.inf, + "Q0": 5.0, + # "Qref": 100.0, + } + ffns_theory = tolist(ffns_theory) + + def benchmark_plain(self, pto, qed): + """Plain configuration""" + + th = self.ffns_theory.copy() + th.update({"PTO": [pto], "QED": [qed]}) + self.run( + cartesian_product(th), + operators.build(operators.apfel_config), + ["CT14qed_proton"], + ) + + +if __name__ == "__main__": + + # obj = BenchmarkVFNS() + obj = BenchmarkFFNS() + + obj.benchmark_plain(1, 1) + # obj.benchmark_sv(2, "exponentiated") + # obj.benchmark_kthr(2) + # obj.benchmark_msbar(2) From 159b59d7985b8e58077f6a625e0a41e6dbf9c8f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 28 Jul 2022 17:29:34 +0200 Subject: [PATCH 090/312] Fix comments --- benchmarks/apfel_bench_qed.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/benchmarks/apfel_bench_qed.py b/benchmarks/apfel_bench_qed.py index b5a66c54a..7b02b1636 100644 --- a/benchmarks/apfel_bench_qed.py +++ b/benchmarks/apfel_bench_qed.py @@ -44,15 +44,14 @@ class BenchmarkFFNS(ApfelBenchmark): "FNS": "FFNS", "ModEv": [ "EXA", - # "EXP", - # "TRN", + # "EXP", + # "TRN", ], "NfFF": 5, "kcThr": 0.0, "kbThr": 0.0, "ktThr": np.inf, "Q0": 5.0, - # "Qref": 100.0, } ffns_theory = tolist(ffns_theory) @@ -70,10 +69,6 @@ def benchmark_plain(self, pto, qed): if __name__ == "__main__": - # obj = BenchmarkVFNS() obj = BenchmarkFFNS() obj.benchmark_plain(1, 1) - # obj.benchmark_sv(2, "exponentiated") - # obj.benchmark_kthr(2) - # obj.benchmark_msbar(2) From 7f794731f9c37c12d7a59ff153a6a89eb65242d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Sun, 31 Jul 2022 13:13:02 +0200 Subject: [PATCH 091/312] Add qed in rotate_matching --- src/eko/evolution_operator/flavors.py | 108 +++++++++++++++++++++----- src/eko/evolution_operator/grid.py | 2 +- 2 files changed, 89 insertions(+), 21 deletions(-) diff --git a/src/eko/evolution_operator/flavors.py b/src/eko/evolution_operator/flavors.py index e8de28637..a7642c3d1 100644 --- a/src/eko/evolution_operator/flavors.py +++ b/src/eko/evolution_operator/flavors.py @@ -8,6 +8,7 @@ import numpy as np from .. import basis_rotation as br +from .. import constants def pids_from_intrinsic_evol(label, nf, normalize): @@ -130,7 +131,7 @@ def rotate_pm_to_flavor(label): return l -def rotate_matching(nf, inverse=False): +def rotate_matching(nf, is_qed=False, inverse=False): """ Rotation between matching basis (with e.g. S,g,...V8 and c+,c-) and new true evolution basis (with S,g,...V8,T15,V15). @@ -150,24 +151,59 @@ def rotate_matching(nf, inverse=False): # the gluon and the photon do not care about new quarks l = {"g.g": 1.0, "ph.ph": 1.0} # already active distributions - for k in range(2, nf): # nf is the upper, so excluded - n = k**2 - 1 - l[f"V{n}.V{n}"] = 1.0 - l[f"T{n}.T{n}"] = 1.0 - # the new contributions - n = nf**2 - 1 # nf is pointing upwards q = br.quark_names[nf - 1] - for (tot, oth, qpm) in (("S", f"T{n}", f"{q}+"), ("V", f"V{n}", f"{q}-")): - if inverse: - l[f"{tot}.{tot}"] = (nf - 1.0) / nf - l[f"{tot}.{oth}"] = 1.0 / nf - l[f"{qpm}.{tot}"] = 1.0 / nf - l[f"{qpm}.{oth}"] = -1.0 / nf - else: - l[f"{tot}.{tot}"] = 1.0 - l[f"{tot}.{qpm}"] = 1.0 - l[f"{oth}.{tot}"] = 1.0 - l[f"{oth}.{qpm}"] = -(nf - 1.0) + if not is_qed: + for k in range(2, nf): # nf is the upper, so excluded + n = k**2 - 1 + l[f"V{n}.V{n}"] = 1.0 + l[f"T{n}.T{n}"] = 1.0 + # the new contributions + n = nf**2 - 1 # nf is pointing upwards + for (tot, oth, qpm) in (("S", f"T{n}", f"{q}+"), ("V", f"V{n}", f"{q}-")): + if inverse: + l[f"{tot}.{tot}"] = (nf - 1.0) / nf + l[f"{tot}.{oth}"] = 1.0 / nf + l[f"{qpm}.{tot}"] = 1.0 / nf + l[f"{qpm}.{oth}"] = -1.0 / nf + else: + l[f"{tot}.{tot}"] = 1.0 + l[f"{tot}.{qpm}"] = 1.0 + l[f"{oth}.{tot}"] = 1.0 + l[f"{oth}.{qpm}"] = -(nf - 1.0) + else: + name = {3: "d3", 4: "u3", 5: "d8", 6: "u8"} + for k in range(3, nf): + l[f"V{name[k]}.V{name[k]}"] = 1.0 + l[f"T{name[k]}.T{name[k]}"] = 1.0 + for (tot, totdelta, oth, qpm) in ( + ("S", "Sdelta", f"T{name[nf]}", f"{q}+"), + ("V", "Vdelta", f"V{name[nf]}", f"{q}-"), + ): + a = a(nf) + b = b(nf) + c = c(nf) + d = d(nf) + if inverse: + den = c - a - b * d + l[f"{tot}.{tot}"] = (c - b * d) / den + l[f"{tot}.{totdelta}"] = b / den + l[f"{tot}.{oth}"] = -1 / den + l[f"{totdelta}.{tot}"] = a * d / den + l[f"{totdelta}.{totdelta}"] = (c - a) / den + l[f"{totdelta}.{oth}"] = -d / den + l[f"{qpm}.{tot}"] = -a / den + l[f"{qpm}.{totdelta}"] = -b / den + l[f"{qpm}.{oth}"] = 1 / den + else: + l[f"{tot}.{tot}"] = 1.0 + l[f"{tot}.{totdelta}"] = 0.0 + l[f"{tot}.{qpm}"] = 1.0 + l[f"{totdelta}.{tot}"] = 0.0 + l[f"{totdelta}.{totdelta}"] = 1.0 + l[f"{totdelta}.{qpm}"] = d + l[f"{oth}.{tot}"] = a + l[f"{oth}.{totdelta}"] = b + l[f"{oth}.{qpm}"] = c # also higher quarks do not care for k in range(nf + 1, 6 + 1): q = br.quark_names[k - 1] @@ -176,8 +212,40 @@ def rotate_matching(nf, inverse=False): return l -def rotate_matching_inverse(nf): - return rotate_matching(nf, True) +def rotate_matching_inverse(nf, is_qed=False): + return rotate_matching(nf, is_qed, True) + + +def a(nf): + if nf in [4, 6]: # heavy flavor is up-like + return constants.uplike_flavors(nf - 1) / (nf - 1) + elif nf in [3, 5]: # heavy flavor is down-like + nd = (nf - 1) - constants.uplike_flavors(nf - 1) + return nd / (nf - 1) + + +def b(nf): + nu_frac_nf = constants.uplike_flavors(nf - 1) / (nf - 1) + if nf in [4, 6]: # heavy flavor is up-like + return nu_frac_nf + elif nf in [3, 5]: # heavy flavor is down-like + return -nu_frac_nf + + +def c(nf): + if nf in [4, 6]: # heavy flavor is up-like + return -1 + elif nf in [3, 5]: # heavy flavor is down-like + return -2 + + +def d(nf): + if nf in [4, 6]: # heavy flavor is up-like + nu = constants.uplike_flavors(nf - 1) + nd = (nf - 1) - nu + return nd / nu + elif nf in [3, 5]: # heavy flavor is down-like + return -1 def pids_from_intrinsic_unified_evol(label, nf, normalize): diff --git a/src/eko/evolution_operator/grid.py b/src/eko/evolution_operator/grid.py index b4a22f148..329c63f1a 100644 --- a/src/eko/evolution_operator/grid.py +++ b/src/eko/evolution_operator/grid.py @@ -290,7 +290,7 @@ def generate(self, q2): intrinsic_range=intrinsic_range, ) rot = member.ScalarOperator.promote_names( - flavors.rotate_matching(op.nf + 1), op.q2_to + flavors.rotate_matching(op.nf + 1, is_qed), op.q2_to ) final_op = final_op @ rot @ matching @ phys_op # is_qed = self.config["order"][1] > 0 From ddf98d2a9106f1b69b0a3932046f3da66c7cc42f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Sun, 31 Jul 2022 13:23:17 +0200 Subject: [PATCH 092/312] Add is_qed in rotate_matching_inverse call --- src/eko/evolution_operator/grid.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eko/evolution_operator/grid.py b/src/eko/evolution_operator/grid.py index 329c63f1a..355a04b0c 100644 --- a/src/eko/evolution_operator/grid.py +++ b/src/eko/evolution_operator/grid.py @@ -279,7 +279,7 @@ def generate(self, q2): intrinsic_range=intrinsic_range, ) invrot = member.ScalarOperator.promote_names( - flavors.rotate_matching_inverse(op.nf), op.q2_to + flavors.rotate_matching_inverse(op.nf, is_qed), op.q2_to ) final_op = final_op @ matching @ invrot @ phys_op else: From 4ee4315b6dfb33750417de8f4fd8e916e4cb769e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Sun, 31 Jul 2022 17:59:39 +0200 Subject: [PATCH 093/312] Remove unnecessary comment --- src/eko/evolution_operator/grid.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/eko/evolution_operator/grid.py b/src/eko/evolution_operator/grid.py index 355a04b0c..68ab72553 100644 --- a/src/eko/evolution_operator/grid.py +++ b/src/eko/evolution_operator/grid.py @@ -293,7 +293,6 @@ def generate(self, q2): flavors.rotate_matching(op.nf + 1, is_qed), op.q2_to ) final_op = final_op @ rot @ matching @ phys_op - # is_qed = self.config["order"][1] > 0 values, errors = final_op.to_flavor_basis_tensor(is_qed) return { "operators": values, From aa7532212ed07644ad10a37e939e05ba82ffbcc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Sun, 31 Jul 2022 19:37:06 +0200 Subject: [PATCH 094/312] Add comments --- src/eko/basis_rotation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/eko/basis_rotation.py b/src/eko/basis_rotation.py index ec005289d..5402ca9d1 100644 --- a/src/eko/basis_rotation.py +++ b/src/eko/basis_rotation.py @@ -89,8 +89,8 @@ unified_evol_basis_pids = tuple( [21, 22, 100, 101, 200, 201] - + [103 + 2, 203 + 2] - + [103 + 1, 203 + 1] + + [103 + 2, 203 + 2] # d = +2 + + [103 + 1, 203 + 1] # u = +1 + [108 + 2, 208 + 2] + [108 + 1, 208 + 1] ) From 676abfdcb67f870f10250ac9fe5ff21602c7abdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Sun, 31 Jul 2022 19:39:38 +0200 Subject: [PATCH 095/312] Change is_qed -> qed --- src/eko/basis_rotation.py | 8 ++++---- src/eko/evolution_operator/flavors.py | 18 +++++++++--------- src/eko/evolution_operator/grid.py | 12 ++++++------ src/eko/evolution_operator/physical.py | 10 +++++----- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/eko/basis_rotation.py b/src/eko/basis_rotation.py index 5402ca9d1..0934765d7 100644 --- a/src/eko/basis_rotation.py +++ b/src/eko/basis_rotation.py @@ -269,7 +269,7 @@ def rotate_flavor_to_unified_evolution(nf): } -def ad_projector(ad_lab, nf, is_qed=False): +def ad_projector(ad_lab, nf, qed=False): """ Build a projector (as a numpy array) for the given anomalous dimension sector. @@ -286,7 +286,7 @@ def ad_projector(ad_lab, nf, is_qed=False): proj : np.ndarray projector over the specified sector """ - if not is_qed: + if not qed: proj = np.zeros_like(rotate_flavor_to_evolution, dtype=float) l = map_ad_to_evolution[ad_lab] basis = evol_basis @@ -314,7 +314,7 @@ def ad_projector(ad_lab, nf, is_qed=False): return proj -def ad_projectors(nf, is_qed=False): +def ad_projectors(nf, qed=False): """ Build projectors tensor (as a numpy array), collecting all the individual sector projectors. @@ -331,7 +331,7 @@ def ad_projectors(nf, is_qed=False): """ projs = [] for ad in anomalous_dimensions_basis: - projs.append(ad_projector(ad, nf, is_qed)) + projs.append(ad_projector(ad, nf, qed)) return np.array(projs) diff --git a/src/eko/evolution_operator/flavors.py b/src/eko/evolution_operator/flavors.py index a7642c3d1..0e703e3a8 100644 --- a/src/eko/evolution_operator/flavors.py +++ b/src/eko/evolution_operator/flavors.py @@ -57,7 +57,7 @@ def pids_from_intrinsic_evol(label, nf, normalize): return weights -def get_range(evol_labels, is_qed=False): +def get_range(evol_labels, qed=False): """ Determine the number of light and heavy flavors participating in the input and output. @@ -74,10 +74,10 @@ def get_range(evol_labels, is_qed=False): nf_in = 3 nf_out = 3 - def update(label, is_qed=False): + def update(label, qed=False): nf = 3 if label[0] == "T": - if not is_qed: + if not qed: nf = round(np.sqrt(int(label[1:]) + 1)) else: if label[1:] == "d3": @@ -93,8 +93,8 @@ def update(label, is_qed=False): return nf for op in evol_labels: - nf_in = max(update(op.input, is_qed), nf_in) - nf_out = max(update(op.target, is_qed), nf_out) + nf_in = max(update(op.input, qed), nf_in) + nf_out = max(update(op.target, qed), nf_out) return nf_in, nf_out @@ -131,7 +131,7 @@ def rotate_pm_to_flavor(label): return l -def rotate_matching(nf, is_qed=False, inverse=False): +def rotate_matching(nf, qed=False, inverse=False): """ Rotation between matching basis (with e.g. S,g,...V8 and c+,c-) and new true evolution basis (with S,g,...V8,T15,V15). @@ -152,7 +152,7 @@ def rotate_matching(nf, is_qed=False, inverse=False): l = {"g.g": 1.0, "ph.ph": 1.0} # already active distributions q = br.quark_names[nf - 1] - if not is_qed: + if not qed: for k in range(2, nf): # nf is the upper, so excluded n = k**2 - 1 l[f"V{n}.V{n}"] = 1.0 @@ -212,8 +212,8 @@ def rotate_matching(nf, is_qed=False, inverse=False): return l -def rotate_matching_inverse(nf, is_qed=False): - return rotate_matching(nf, is_qed, True) +def rotate_matching_inverse(nf, qed=False): + return rotate_matching(nf, qed, True) def a(nf): diff --git a/src/eko/evolution_operator/grid.py b/src/eko/evolution_operator/grid.py index 68ab72553..d74514044 100644 --- a/src/eko/evolution_operator/grid.py +++ b/src/eko/evolution_operator/grid.py @@ -260,14 +260,14 @@ def generate(self, q2): is_downward = is_downward_path(path) if is_downward: intrinsic_range = [4, 5, 6] - is_qed = self.config["order"][1] > 0 + qed = self.config["order"][1] > 0 final_op = physical.PhysicalOperator.ad_to_evol_map( - operator.op_members, operator.nf, operator.q2_to, intrinsic_range, is_qed + operator.op_members, operator.nf, operator.q2_to, intrinsic_range, qed ) # and multiply the lower ones from the right for op in reversed(list(thr_ops)): phys_op = physical.PhysicalOperator.ad_to_evol_map( - op.op_members, op.nf, op.q2_to, intrinsic_range, is_qed + op.op_members, op.nf, op.q2_to, intrinsic_range, qed ) # join with the basis rotation, since matching requires c+ (or likewise) @@ -279,7 +279,7 @@ def generate(self, q2): intrinsic_range=intrinsic_range, ) invrot = member.ScalarOperator.promote_names( - flavors.rotate_matching_inverse(op.nf, is_qed), op.q2_to + flavors.rotate_matching_inverse(op.nf, qed), op.q2_to ) final_op = final_op @ matching @ invrot @ phys_op else: @@ -290,10 +290,10 @@ def generate(self, q2): intrinsic_range=intrinsic_range, ) rot = member.ScalarOperator.promote_names( - flavors.rotate_matching(op.nf + 1, is_qed), op.q2_to + flavors.rotate_matching(op.nf + 1, qed), op.q2_to ) final_op = final_op @ rot @ matching @ phys_op - values, errors = final_op.to_flavor_basis_tensor(is_qed) + values, errors = final_op.to_flavor_basis_tensor(qed) return { "operators": values, "operator_errors": errors, diff --git a/src/eko/evolution_operator/physical.py b/src/eko/evolution_operator/physical.py index 4c08ad082..f44696377 100644 --- a/src/eko/evolution_operator/physical.py +++ b/src/eko/evolution_operator/physical.py @@ -25,7 +25,7 @@ class PhysicalOperator(member.OperatorBase): """ @classmethod - def ad_to_evol_map(cls, op_members, nf, q2_final, intrinsic_range, is_qed=False): + def ad_to_evol_map(cls, op_members, nf, q2_final, intrinsic_range, qed=False): """ Obtain map between the 3-dimensional anomalous dimension basis and the 4-dimensional evolution basis. @@ -53,7 +53,7 @@ def ad_to_evol_map(cls, op_members, nf, q2_final, intrinsic_range, is_qed=False) "g.g": op_members[(21, 21)], "g.S": op_members[(21, 100)], } - if not is_qed: + if not qed: m.update({"V.V": op_members[(br.non_singlet_pids_map["nsV"], 0)]}) # add elements which are already active for f in range(2, nf + 1): @@ -117,7 +117,7 @@ def ad_to_evol_map(cls, op_members, nf, q2_final, intrinsic_range, is_qed=False) # map key to MemberName return cls.promote_names(m, q2_final) - def to_flavor_basis_tensor(self, is_qed=False): + def to_flavor_basis_tensor(self, qed=False): """ Convert the computations into an rank 4 tensor over flavor operator space and momentum fraction operator space. @@ -127,14 +127,14 @@ def to_flavor_basis_tensor(self, is_qed=False): tensor : numpy.ndarray EKO """ - nf_in, nf_out = flavors.get_range(self.op_members.keys(), is_qed) + nf_in, nf_out = flavors.get_range(self.op_members.keys(), qed) len_pids = len(br.flavor_basis_pids) len_xgrid = list(self.op_members.values())[0].value.shape[0] # dimension will be pids^2 * xgrid^2 value_tensor = np.zeros((len_pids, len_xgrid, len_pids, len_xgrid)) error_tensor = value_tensor.copy() for name, op in self.op_members.items(): - if not is_qed: + if not qed: in_pids = flavors.pids_from_intrinsic_evol(name.input, nf_in, False) out_pids = flavors.pids_from_intrinsic_evol(name.target, nf_out, True) else: From e22399bca3376602a6273ec801e047dbdef00e6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Sun, 31 Jul 2022 21:47:09 +0200 Subject: [PATCH 096/312] Add qed in split_ad_to_evol_map --- src/eko/evolution_operator/flavors.py | 10 ++++----- src/eko/evolution_operator/grid.py | 2 ++ src/eko/matching_conditions/__init__.py | 21 +++++++++++++++---- .../operator_matrix_element.py | 3 ++- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/eko/evolution_operator/flavors.py b/src/eko/evolution_operator/flavors.py index 0e703e3a8..3934096b3 100644 --- a/src/eko/evolution_operator/flavors.py +++ b/src/eko/evolution_operator/flavors.py @@ -171,13 +171,13 @@ def rotate_matching(nf, qed=False, inverse=False): l[f"{oth}.{tot}"] = 1.0 l[f"{oth}.{qpm}"] = -(nf - 1.0) else: - name = {3: "d3", 4: "u3", 5: "d8", 6: "u8"} + names = {3: "d3", 4: "u3", 5: "d8", 6: "u8"} for k in range(3, nf): - l[f"V{name[k]}.V{name[k]}"] = 1.0 - l[f"T{name[k]}.T{name[k]}"] = 1.0 + l[f"V{names[k]}.V{names[k]}"] = 1.0 + l[f"T{names[k]}.T{names[k]}"] = 1.0 for (tot, totdelta, oth, qpm) in ( - ("S", "Sdelta", f"T{name[nf]}", f"{q}+"), - ("V", "Vdelta", f"V{name[nf]}", f"{q}-"), + ("S", "Sdelta", f"T{names[nf]}", f"{q}+"), + ("V", "Vdelta", f"V{names[nf]}", f"{q}-"), ): a = a(nf) b = b(nf) diff --git a/src/eko/evolution_operator/grid.py b/src/eko/evolution_operator/grid.py index d74514044..015a1eb09 100644 --- a/src/eko/evolution_operator/grid.py +++ b/src/eko/evolution_operator/grid.py @@ -277,6 +277,7 @@ def generate(self, q2): op.nf - 1, op.q2_to, intrinsic_range=intrinsic_range, + qed=qed, ) invrot = member.ScalarOperator.promote_names( flavors.rotate_matching_inverse(op.nf, qed), op.q2_to @@ -288,6 +289,7 @@ def generate(self, q2): op.nf, op.q2_to, intrinsic_range=intrinsic_range, + qed=qed, ) rot = member.ScalarOperator.promote_names( flavors.rotate_matching(op.nf + 1, qed), op.q2_to diff --git a/src/eko/matching_conditions/__init__.py b/src/eko/matching_conditions/__init__.py index 033f3a6f7..c9140ecbd 100644 --- a/src/eko/matching_conditions/__init__.py +++ b/src/eko/matching_conditions/__init__.py @@ -24,6 +24,7 @@ def split_ad_to_evol_map( nf, q2_thr, intrinsic_range, + qed=False, ): """ Create the instance from the |OME|. @@ -50,10 +51,22 @@ def split_ad_to_evol_map( } # add elements which are already active - for f in range(2, nf + 1): - n = f**2 - 1 - m[f"V{n}.V{n}"] = m["V.V"] - m[f"T{n}.T{n}"] = m["V.V"] + if not qed: + for f in range(2, nf + 1): + n = f**2 - 1 + m[f"V{n}.V{n}"] = m["V.V"] + m[f"T{n}.T{n}"] = m["V.V"] + else: + m.update( + { + "Sdelta.Sdelta": op_members[(200, 200)], + "Vdelta.Vdelta": op_members[(200, 200)], + } + ) + names = {3: "d3", 4: "u3", 5: "d8", 6: "u8"} + for k in range(3, nf + 1): + m[f"V{names[k]}.V{names[k]}"] = m["V.V"] + m[f"T{names[k]}.T{names[k]}"] = m["V.V"] # activate the next heavy quark hq = br.quark_names[nf] diff --git a/src/eko/matching_conditions/operator_matrix_element.py b/src/eko/matching_conditions/operator_matrix_element.py index 430c0b590..505155960 100644 --- a/src/eko/matching_conditions/operator_matrix_element.py +++ b/src/eko/matching_conditions/operator_matrix_element.py @@ -219,7 +219,7 @@ def quad_ker( ) # compute the ome - if ker_base.is_singlet: + if ker_base.is_singlet or ker_base.is_QEDsinglet: indices = {21: 0, 100: 1, 90: 2} A = A_singlet(order, ker_base.n, sx, nf, L, is_msbar, sx_ns) else: @@ -273,6 +273,7 @@ class OperatorMatrixElement(Operator): (br.matching_hminus_pid, 200), (br.matching_hminus_pid, br.matching_hminus_pid), ] + # still valid in QED since Sdelta and Vdelta matchings are diagonal def __init__(self, config, managers, nf, q2, is_backward, L, is_msbar): super().__init__(config, managers, nf, q2, None) From 5b4501e18c18511c06f98323255f4c97c1ae43d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 3 Aug 2022 19:43:02 +0200 Subject: [PATCH 097/312] Fix rotate_matching --- src/eko/evolution_operator/flavors.py | 116 +++++++++++++++----------- 1 file changed, 68 insertions(+), 48 deletions(-) diff --git a/src/eko/evolution_operator/flavors.py b/src/eko/evolution_operator/flavors.py index 3934096b3..25af212d2 100644 --- a/src/eko/evolution_operator/flavors.py +++ b/src/eko/evolution_operator/flavors.py @@ -179,31 +179,29 @@ def rotate_matching(nf, qed=False, inverse=False): ("S", "Sdelta", f"T{names[nf]}", f"{q}+"), ("V", "Vdelta", f"V{names[nf]}", f"{q}-"), ): - a = a(nf) - b = b(nf) - c = c(nf) - d = d(nf) + # TODO : double check it + a, b, c, d, e, f = rotation_parameters(nf) if inverse: - den = c - a - b * d - l[f"{tot}.{tot}"] = (c - b * d) / den - l[f"{tot}.{totdelta}"] = b / den - l[f"{tot}.{oth}"] = -1 / den - l[f"{totdelta}.{tot}"] = a * d / den - l[f"{totdelta}.{totdelta}"] = (c - a) / den - l[f"{totdelta}.{oth}"] = -d / den - l[f"{qpm}.{tot}"] = -a / den - l[f"{qpm}.{totdelta}"] = -b / den - l[f"{qpm}.{oth}"] = 1 / den + den = -b * d + a * e - c * e + b * f + l[f"{tot}.{tot}"] = (c * e - b * f) / den + l[f"{tot}.{totdelta}"] = e / den + l[f"{tot}.{oth}"] = -b / den + l[f"{totdelta}.{tot}"] = (c * d - a * f) / den + l[f"{totdelta}.{totdelta}"] = (f - d) / den + l[f"{totdelta}.{oth}"] = (a - c) / den + l[f"{qpm}.{tot}"] = (-b * d + a * e) / den + l[f"{qpm}.{totdelta}"] = -e / den + l[f"{qpm}.{oth}"] = b / den else: l[f"{tot}.{tot}"] = 1.0 l[f"{tot}.{totdelta}"] = 0.0 l[f"{tot}.{qpm}"] = 1.0 - l[f"{totdelta}.{tot}"] = 0.0 - l[f"{totdelta}.{totdelta}"] = 1.0 - l[f"{totdelta}.{qpm}"] = d - l[f"{oth}.{tot}"] = a - l[f"{oth}.{totdelta}"] = b - l[f"{oth}.{qpm}"] = c + l[f"{totdelta}.{tot}"] = a + l[f"{totdelta}.{totdelta}"] = b + l[f"{totdelta}.{qpm}"] = c + l[f"{oth}.{tot}"] = d + l[f"{oth}.{totdelta}"] = e + l[f"{oth}.{qpm}"] = f # also higher quarks do not care for k in range(nf + 1, 6 + 1): q = br.quark_names[k - 1] @@ -216,36 +214,58 @@ def rotate_matching_inverse(nf, qed=False): return rotate_matching(nf, qed, True) -def a(nf): +def rotation_parameters(nf): + nu_l = constants.uplike_flavors(nf - 1) + nd_l = (nf - 1) - nu_l + nu_h = constants.uplike_flavors(nf) + nd_h = nf - nu_h + a = nd_l / (nf - 1) * (nd_h / nu_h - 1) + b = (nd_h / nu_h * nd_l - nu_l) / nf if nf in [4, 6]: # heavy flavor is up-like - return constants.uplike_flavors(nf - 1) / (nf - 1) + c = nd_l / nu_l + d = nu_l / (nf - 1) + e = nu_l / (nf - 1) elif nf in [3, 5]: # heavy flavor is down-like - nd = (nf - 1) - constants.uplike_flavors(nf - 1) - return nd / (nf - 1) - - -def b(nf): - nu_frac_nf = constants.uplike_flavors(nf - 1) / (nf - 1) - if nf in [4, 6]: # heavy flavor is up-like - return nu_frac_nf - elif nf in [3, 5]: # heavy flavor is down-like - return -nu_frac_nf - - -def c(nf): - if nf in [4, 6]: # heavy flavor is up-like - return -1 - elif nf in [3, 5]: # heavy flavor is down-like - return -2 - - -def d(nf): - if nf in [4, 6]: # heavy flavor is up-like - nu = constants.uplike_flavors(nf - 1) - nd = (nf - 1) - nu - return nd / nu - elif nf in [3, 5]: # heavy flavor is down-like - return -1 + c = -1 + d = nd_l / (nf - 1) + e = -nu_l / (nf - 1) + if nf in [3, 4]: # s and c unlock T3d, T3u + f = -1 + elif nf in [5, 6]: # b and t unlock T8d, T8u + f = -2 + return a, b, c, d, e, f + + +# def a(nf): +# if nf in [4, 6]: # heavy flavor is up-like +# return constants.uplike_flavors(nf - 1) / (nf - 1) +# elif nf in [3, 5]: # heavy flavor is down-like +# nd = (nf - 1) - constants.uplike_flavors(nf - 1) +# return nd / (nf - 1) + + +# def b(nf): +# nu_frac_nf = constants.uplike_flavors(nf - 1) / (nf - 1) +# if nf in [4, 6]: # heavy flavor is up-like +# return nu_frac_nf +# elif nf in [3, 5]: # heavy flavor is down-like +# return -nu_frac_nf + + +# def c(nf): +# if nf in [4, 6]: # heavy flavor is up-like +# return -1 +# elif nf in [3, 5]: # heavy flavor is down-like +# return -2 + + +# def d(nf): +# if nf in [4, 6]: # heavy flavor is up-like +# nu = constants.uplike_flavors(nf - 1) +# nd = (nf - 1) - nu +# return nd / nu +# elif nf in [3, 5]: # heavy flavor is down-like +# return -1 def pids_from_intrinsic_unified_evol(label, nf, normalize): From 9f90f5dec426f06352a174ea9fb9b15f9cb1a076 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 12 Aug 2022 18:38:39 +0200 Subject: [PATCH 098/312] Fix rotation_parameters --- src/eko/evolution_operator/flavors.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/eko/evolution_operator/flavors.py b/src/eko/evolution_operator/flavors.py index 25af212d2..17fa245ff 100644 --- a/src/eko/evolution_operator/flavors.py +++ b/src/eko/evolution_operator/flavors.py @@ -215,6 +215,19 @@ def rotate_matching_inverse(nf, qed=False): def rotation_parameters(nf): + """Parameters of the basis rotation from (S, Sdelta, h+) + into (S, Sdelta, T_i), or equivalentely for V, Vdelta, V_i, h-. + + Parameters + ---------- + nf : int + number of active flavors in the higher patch: to activate T3u V3u nf=4 + + Returns + ------- + a,b,c,d,e,f : float + Parameters of the rotation: Sdelta = a*S + b*Sdelta + c*h+ + """ nu_l = constants.uplike_flavors(nf - 1) nd_l = (nf - 1) - nu_l nu_h = constants.uplike_flavors(nf) @@ -229,10 +242,12 @@ def rotation_parameters(nf): c = -1 d = nd_l / (nf - 1) e = -nu_l / (nf - 1) - if nf in [3, 4]: # s and c unlock T3d, T3u + if nf in [3, 4]: f = -1 - elif nf in [5, 6]: # b and t unlock T8d, T8u + # s and c unlock T3d, T3u that have -h+ + elif nf in [5, 6]: f = -2 + # b and t unlock T8d, T8u that have -2h+ return a, b, c, d, e, f From d2fda09a98d83a84e48ba89f0a28acd5ed3a85ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Sun, 14 Aug 2022 14:06:50 +0200 Subject: [PATCH 099/312] Add full_labels_qed in OperatorMatrixElement --- src/eko/basis_rotation.py | 31 ++++++++++++------- .../operator_matrix_element.py | 22 ++++++------- 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/src/eko/basis_rotation.py b/src/eko/basis_rotation.py index 0934765d7..dff253846 100644 --- a/src/eko/basis_rotation.py +++ b/src/eko/basis_rotation.py @@ -1,8 +1,5 @@ # -*- coding: utf-8 -*- -""" -This module contains the definitions of the -:doc:`Flavor Basis and Evolution Basis `. -""" +"""Contains the definitions of the :doc:`Flavor Basis and Evolution Basis `.""" import numpy as np @@ -111,12 +108,11 @@ (non_singlet_pids_map["ns+"], 0), (non_singlet_pids_map["nsV"], 0), ) -# Sdelta = 101 singlet_unified_labels = ( (21, 21), (21, 22), (21, 100), - (21, 101), + (21, 101), # Sdelta = 101 (22, 21), (22, 22), (22, 100), @@ -132,8 +128,8 @@ ) valence_unified_labels = ( (10200, 10200), - (10200, 10204), - (10204, 10200), # Vdelta = 10204 + (10200, 10204), # Vdelta = 10204 + (10204, 10200), (10204, 10204), ) non_singlet_unified_labels = ( @@ -181,6 +177,19 @@ # Tranformation from physical basis to QCDxQED evolution basis def rotate_flavor_to_unified_evolution(nf): + """ + Basis rotation matrix between :doc:`Flavor Basis and Evolution Basis `. + + Parameters + ---------- + nf : int + number of light flavors + + Returns + ------- + rot : np.ndarray + matrix rotation from flavor to unified evolution basis + """ nu = constants.uplike_flavors(nf) nd = nf - nu rn = nd / nu @@ -271,8 +280,7 @@ def rotate_flavor_to_unified_evolution(nf): def ad_projector(ad_lab, nf, qed=False): """ - Build a projector (as a numpy array) for the given anomalous dimension - sector. + Build a projector (as a numpy array) for the given anomalous dimension sector. Parameters ---------- @@ -316,8 +324,7 @@ def ad_projector(ad_lab, nf, qed=False): def ad_projectors(nf, qed=False): """ - Build projectors tensor (as a numpy array), collecting all the individual - sector projectors. + Build projectors tensor (as a numpy array), collecting all the individual sector projectors. Parameters ---------- diff --git a/src/eko/matching_conditions/operator_matrix_element.py b/src/eko/matching_conditions/operator_matrix_element.py index 505155960..0b24f5970 100644 --- a/src/eko/matching_conditions/operator_matrix_element.py +++ b/src/eko/matching_conditions/operator_matrix_element.py @@ -1,9 +1,7 @@ # -*- coding: utf-8 -*- -""" -This module defines the |OME| for the non-trivial matching conditions in the -|VFNS| evolution. -""" +"""Defines the |OME| for the non-trivial matching conditions in the |VFNS| evolution.""" +import copy import functools import logging @@ -21,7 +19,7 @@ @nb.njit(cache=True) def A_singlet(matching_order, n, sx, nf, L, is_msbar, sx_ns=None): r""" - Computes the tower of the singlet |OME|. + Compute the tower of the singlet |OME|. Parameters ---------- @@ -65,7 +63,7 @@ def A_singlet(matching_order, n, sx, nf, L, is_msbar, sx_ns=None): @nb.njit(cache=True) def A_non_singlet(matching_order, n, sx, nf, L): r""" - Computes the tower of the non-singlet |OME| + Compute the tower of the non-singlet |OME|. Parameters ---------- @@ -157,7 +155,7 @@ def quad_ker( u, order, mode0, mode1, is_log, logx, areas, a_s, nf, L, backward_method, is_msbar ): """ - Raw kernel inside quad + Raw kernel inside quad. Parameters ---------- @@ -274,6 +272,7 @@ class OperatorMatrixElement(Operator): (br.matching_hminus_pid, br.matching_hminus_pid), ] # still valid in QED since Sdelta and Vdelta matchings are diagonal + full_labels_qed = copy.deepcopy(full_labels) def __init__(self, config, managers, nf, q2, is_backward, L, is_msbar): super().__init__(config, managers, nf, q2, None) @@ -289,14 +288,13 @@ def __init__(self, config, managers, nf, q2, is_backward, L, is_msbar): @property def labels(self): - """Computes the necessary sector labels to compute. + """Compute the necessary sector labels to compute. Returns ------- list(str) sector labels """ - labels = [] # non-singlet labels if self.config["debug_skip_non_singlet"]: @@ -330,7 +328,7 @@ def labels(self): return labels def quad_ker(self, label, logx, areas): - """Partially initialized integrand function. + """Compute partially initialized integrand function. Parameters ---------- @@ -363,7 +361,7 @@ def quad_ker(self, label, logx, areas): @property def a_s(self): - """Returns the computed values for :math:`a_s`. + """Return the computed values for :math:`a_s`. Note that here you need to use :math:`a_s^{n_f+1}` """ @@ -371,7 +369,7 @@ def a_s(self): return sc.a_s(self.mur2_shift(self.q2_from), self.q2_from, nf_to=self.nf + 1) def compute(self): - """Compute the actual operators (i.e. run the integrations)""" + """Compute the actual operators (i.e. run the integrations).""" self.initialize_op_members() # At LO you don't need anything else From 65b2e99be04bb97f22fe16e7c77b62511f66e05e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Sun, 14 Aug 2022 17:19:45 +0200 Subject: [PATCH 100/312] Implement to_uni_evol in output.py --- src/eko/output.py | 50 ++++++++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/src/eko/output.py b/src/eko/output.py index c6a16af87..beafaa941 100644 --- a/src/eko/output.py +++ b/src/eko/output.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -""" -This file contains the output management -""" +"""Contains the output management.""" import io import logging import pathlib @@ -20,14 +18,11 @@ class Output(dict): - """ - Wrapper for the output to help with application - to PDFs and dumping to file. - """ + """Wrapper for the output to help with application to PDFs and dumping to file.""" def xgrid_reshape(self, targetgrid=None, inputgrid=None): """ - Changes the operators to have in the output targetgrid and/or in the input inputgrid. + Change the operators to have in the output targetgrid and/or in the input inputgrid. The operation is in-place. @@ -99,7 +94,7 @@ def xgrid_reshape(self, targetgrid=None, inputgrid=None): def flavor_reshape(self, targetbasis=None, inputbasis=None): """ - Changes the operators to have in the output targetbasis and/or in the input inputbasis. + Change the operators to have in the output targetbasis and/or in the input inputbasis. The operation is in-place. @@ -177,6 +172,29 @@ def to_evol(self, source=True, target=False): if target: self["targetpids"] = br.evol_basis_pids + def to_uni_evol(self, nf, source=True, target=False): + """ + Rotate the operator into unified evolution basis. + + This also assigns also the pids. The operation is in-place. + + Parameters + ---------- + source : bool + rotate on the input tensor + target : bool + rotate on the output tensor + """ + # rotate + inputbasis = br.rotate_flavor_to_unified_evolution(nf) if source else None + targetbasis = br.rotate_flavor_to_unified_evolution(nf) if target else None + self.flavor_reshape(inputbasis=inputbasis, targetbasis=targetbasis) + # assign pids + if source: + self["inputpids"] = br.unified_evol_basis_pids + if target: + self["targetpids"] = br.unified_evol_basis_pids + def get_raw(self, binarize=True, skip_q2_grid=False): """ Serialize result as dict/YAML. @@ -252,7 +270,7 @@ def dump_yaml(self, stream=None, binarize=True, skip_q2_grid=False): def dump_yaml_to_file(self, filename, binarize=True, skip_q2_grid=False): """ - Writes YAML representation to a file. + Write YAML representation to a file. Parameters ---------- @@ -275,10 +293,7 @@ def dump_yaml_to_file(self, filename, binarize=True, skip_q2_grid=False): def dump_tar(self, tarname): """ - Writes representation into a tar archive containing: - - - metadata (in YAML) - - operator (in numpy ``.npy`` format) + Write representation into a tar archive containing metadata (in YAML) and operator (in numpy ``.npy`` format). Parameters ---------- @@ -315,7 +330,7 @@ def dump_tar(self, tarname): @classmethod def load_yaml(cls, stream, skip_q2_grid=False): """ - Load YAML representation from stream + Load YAML representation from stream. Parameters ---------- @@ -355,7 +370,7 @@ def load_yaml(cls, stream, skip_q2_grid=False): @classmethod def load_yaml_from_file(cls, filename, skip_q2_grid=False): """ - Load YAML representation from file + Load YAML representation from file. Parameters ---------- @@ -378,8 +393,7 @@ def load_yaml_from_file(cls, filename, skip_q2_grid=False): @classmethod def load_tar(cls, tarname): """ - Load tar representation from file (compliant with :meth:`dump_tar` - output). + Load tar representation from file (compliant with :meth:`dump_tar` output). Parameters ---------- From 8e8f33803df5a85c7bbbe858512845eea6ca1e3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 16 Aug 2022 12:06:59 +0200 Subject: [PATCH 101/312] Remove comments in flavors.py --- benchmarks/apfel_bench_qed.py | 2 +- src/eko/evolution_operator/flavors.py | 67 +++++++++------------------ 2 files changed, 24 insertions(+), 45 deletions(-) diff --git a/benchmarks/apfel_bench_qed.py b/benchmarks/apfel_bench_qed.py index 7b02b1636..c0fe997d0 100644 --- a/benchmarks/apfel_bench_qed.py +++ b/benchmarks/apfel_bench_qed.py @@ -34,7 +34,7 @@ class ApfelBenchmark(Runner): @staticmethod def skip_pdfs(_theory): # pdf to skip - return [-6, 6, "T35", "V35"] + return [-6, 6, "Tu8", "Vu8"] class BenchmarkFFNS(ApfelBenchmark): diff --git a/src/eko/evolution_operator/flavors.py b/src/eko/evolution_operator/flavors.py index 17fa245ff..6f2d7e438 100644 --- a/src/eko/evolution_operator/flavors.py +++ b/src/eko/evolution_operator/flavors.py @@ -1,9 +1,5 @@ # -*- coding: utf-8 -*- -r""" -The write-up of the matching conditions is given in -:doc:`Matching Conditions `. - -""" +r"""The write-up of the matching conditions is given in :doc:`Matching Conditions `.""" import numpy as np @@ -13,7 +9,7 @@ def pids_from_intrinsic_evol(label, nf, normalize): r""" - Obtain the list of pids with their corresponding weight, that are contributing to ``evol`` + Obtain the list of pids with their corresponding weight, that are contributing to ``evol``. The normalization of the weights is only needed for the output rotation: @@ -133,13 +129,14 @@ def rotate_pm_to_flavor(label): def rotate_matching(nf, qed=False, inverse=False): """ - Rotation between matching basis (with e.g. S,g,...V8 and c+,c-) and new true evolution basis - (with S,g,...V8,T15,V15). + Rotation between matching basis (with e.g. S,g,...V8 and c+,c-) and new true evolution basis (with S,g,...V8,T15,V15). Parameters ---------- nf : int number of active flavors in the higher patch: to activate T15, nf=4 + qed : bool + use qed? inverse : bool use inverse conditions? @@ -211,12 +208,27 @@ def rotate_matching(nf, qed=False, inverse=False): def rotate_matching_inverse(nf, qed=False): + """ + Inverse rotation between matching basis (with e.g. S,g,...V8 and c+,c-) and new true evolution basis (with S,g,...V8,T15,V15). + + Parameters + ---------- + nf : int + number of active flavors in the higher patch: to activate T15, nf=4 + qed : bool + use qed? + + Returns + ------- + l : dict + mapping in dot notation between the bases + """ return rotate_matching(nf, qed, True) def rotation_parameters(nf): - """Parameters of the basis rotation from (S, Sdelta, h+) - into (S, Sdelta, T_i), or equivalentely for V, Vdelta, V_i, h-. + """ + Parameters of the basis rotation from (S, Sdelta, h+) into (S, Sdelta, T_i), or equivalentely for V, Vdelta, V_i, h-. Parameters ---------- @@ -251,42 +263,9 @@ def rotation_parameters(nf): return a, b, c, d, e, f -# def a(nf): -# if nf in [4, 6]: # heavy flavor is up-like -# return constants.uplike_flavors(nf - 1) / (nf - 1) -# elif nf in [3, 5]: # heavy flavor is down-like -# nd = (nf - 1) - constants.uplike_flavors(nf - 1) -# return nd / (nf - 1) - - -# def b(nf): -# nu_frac_nf = constants.uplike_flavors(nf - 1) / (nf - 1) -# if nf in [4, 6]: # heavy flavor is up-like -# return nu_frac_nf -# elif nf in [3, 5]: # heavy flavor is down-like -# return -nu_frac_nf - - -# def c(nf): -# if nf in [4, 6]: # heavy flavor is up-like -# return -1 -# elif nf in [3, 5]: # heavy flavor is down-like -# return -2 - - -# def d(nf): -# if nf in [4, 6]: # heavy flavor is up-like -# nu = constants.uplike_flavors(nf - 1) -# nd = (nf - 1) - nu -# return nd / nu -# elif nf in [3, 5]: # heavy flavor is down-like -# return -1 - - def pids_from_intrinsic_unified_evol(label, nf, normalize): r""" - Obtain the list of pids with their corresponding weight, that are contributing to intrinsic - unified evolution. + Obtain the list of pids with their corresponding weight, that are contributing to intrinsic unified evolution. Parameters ---------- From aa604321e42d912c4215f15d86bb96a996d1a648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Sat, 20 Aug 2022 20:10:38 +0200 Subject: [PATCH 102/312] Add rotation to unified evolution basis --- src/eko/basis_rotation.py | 58 +++++++++------------- src/eko/output.py | 6 +-- src/ekomark/apply.py | 27 ++++++++-- src/ekomark/benchmark/runner.py | 87 ++++++++++++++++++++++++++++----- 4 files changed, 123 insertions(+), 55 deletions(-) diff --git a/src/eko/basis_rotation.py b/src/eko/basis_rotation.py index dff253846..538e5ca55 100644 --- a/src/eko/basis_rotation.py +++ b/src/eko/basis_rotation.py @@ -176,41 +176,27 @@ """ # Tranformation from physical basis to QCDxQED evolution basis -def rotate_flavor_to_unified_evolution(nf): - """ - Basis rotation matrix between :doc:`Flavor Basis and Evolution Basis `. - - Parameters - ---------- - nf : int - number of light flavors - - Returns - ------- - rot : np.ndarray - matrix rotation from flavor to unified evolution basis - """ - nu = constants.uplike_flavors(nf) - nd = nf - nu - rn = nd / nu - return np.array( - [ - [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], - [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1], - [0, rn, -1, rn, -1, rn, -1, 0, -1, rn, -1, rn, -1, rn], - [0, -1, -1, -1, -1, -1, -1, 0, 1, 1, 1, 1, 1, 1], - [0, -rn, 1, -rn, 1, -rn, 1, 0, -1, rn, -1, rn, -1, rn], - [0, 0, 0, 0, -1, 0, 1, 0, 1, 0, -1, 0, 0, 0], - [0, 0, 0, 0, 1, 0, -1, 0, 1, 0, -1, 0, 0, 0], - [0, 0, 0, -1, 0, 1, 0, 0, 0, 1, 0, -1, 0, 0], - [0, 0, 0, 1, 0, -1, 0, 0, 0, 1, 0, -1, 0, 0], - [0, 0, -2, 0, 1, 0, 1, 0, 1, 0, 1, 0, -2, 0], - [0, 0, 2, 0, -1, 0, -1, 0, 1, 0, 1, 0, -2, 0], - [0, -2, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, -2], - [0, 2, 0, -1, 0, -1, 0, 0, 0, 1, 0, 1, 0, -2], - ] - ) +rotate_flavor_to_unified_evolution = np.array( + [ + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], + [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1], + [0, 1, -1, 1, -1, 1, -1, 0, -1, 1, -1, 1, -1, 1], + [0, -1, -1, -1, -1, -1, -1, 0, 1, 1, 1, 1, 1, 1], + [0, -1, 1, -1, 1, -1, 1, 0, -1, 1, -1, 1, -1, 1], + [0, 0, 0, 0, -1, 0, 1, 0, 1, 0, -1, 0, 0, 0], + [0, 0, 0, 0, 1, 0, -1, 0, 1, 0, -1, 0, 0, 0], + [0, 0, 0, -1, 0, 1, 0, 0, 0, 1, 0, -1, 0, 0], + [0, 0, 0, 1, 0, -1, 0, 0, 0, 1, 0, -1, 0, 0], + [0, 0, -2, 0, 1, 0, 1, 0, 1, 0, 1, 0, -2, 0], + [0, 0, 2, 0, -1, 0, -1, 0, 1, 0, 1, 0, -2, 0], + [0, -2, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, -2], + [0, 2, 0, -1, 0, -1, 0, 0, 0, 1, 0, 1, 0, -2], + ] +) +""" +Basis rotation matrix between :doc:`Flavor Basis and Unified Evolution Basis `. +""" map_ad_to_evolution = { @@ -303,7 +289,7 @@ def ad_projector(ad_lab, nf, qed=False): proj = np.zeros_like(rotate_flavor_to_unified_evolution, dtype=float) l = map_ad_to_unified_evolution[ad_lab] basis = unified_evol_basis - rotate = rotate_flavor_to_unified_evolution(nf) + rotate = rotate_flavor_to_unified_evolution # restrict the evolution basis to light flavors # NOTE: the cut is only needed for "NS_p" and "NS_m", but the other lists # are 1-long so they are unaffected diff --git a/src/eko/output.py b/src/eko/output.py index beafaa941..abfa217ea 100644 --- a/src/eko/output.py +++ b/src/eko/output.py @@ -172,7 +172,7 @@ def to_evol(self, source=True, target=False): if target: self["targetpids"] = br.evol_basis_pids - def to_uni_evol(self, nf, source=True, target=False): + def to_uni_evol(self, source=True, target=False): """ Rotate the operator into unified evolution basis. @@ -186,8 +186,8 @@ def to_uni_evol(self, nf, source=True, target=False): rotate on the output tensor """ # rotate - inputbasis = br.rotate_flavor_to_unified_evolution(nf) if source else None - targetbasis = br.rotate_flavor_to_unified_evolution(nf) if target else None + inputbasis = br.rotate_flavor_to_unified_evolution if source else None + targetbasis = br.rotate_flavor_to_unified_evolution if target else None self.flavor_reshape(inputbasis=inputbasis, targetbasis=targetbasis) # assign pids if source: diff --git a/src/ekomark/apply.py b/src/ekomark/apply.py index fc216ba62..61d4bf237 100644 --- a/src/ekomark/apply.py +++ b/src/ekomark/apply.py @@ -1,11 +1,18 @@ # -*- coding: utf-8 -*- +"""Apply all available operators to the input PDFs.""" import numpy as np from eko import basis_rotation as br from eko import interpolation -def apply_pdf(output, lhapdf_like, targetgrid=None, rotate_to_evolution_basis=False): +def apply_pdf( + output, + lhapdf_like, + targetgrid=None, + rotate_to_evolution_basis=False, + qed=False, +): """ Apply all available operators to the input PDFs. @@ -27,13 +34,19 @@ def apply_pdf(output, lhapdf_like, targetgrid=None, rotate_to_evolution_basis=Fa output PDFs and their associated errors for the computed Q2grid """ if rotate_to_evolution_basis: + if not qed: + rotate_flavor_to_evolution = br.rotate_flavor_to_evolution + else: + rotate_flavor_to_evolution = br.rotate_flavor_to_unified_evolution return apply_pdf_flavor( - output, lhapdf_like, targetgrid, br.rotate_flavor_to_evolution + output, lhapdf_like, targetgrid, rotate_flavor_to_evolution, qed ) return apply_pdf_flavor(output, lhapdf_like, targetgrid) -def apply_pdf_flavor(output, lhapdf_like, targetgrid=None, flavor_rotation=None): +def apply_pdf_flavor( + output, lhapdf_like, targetgrid=None, flavor_rotation=None, qed=False +): """ Apply all available operators to the input PDFs. @@ -85,8 +98,12 @@ def apply_pdf_flavor(output, lhapdf_like, targetgrid=None, flavor_rotation=None) errors = flavor_rotation @ np.array( [op["errors"][pid] for pid in br.flavor_basis_pids] ) - op["pdfs"] = dict(zip(br.evol_basis, pdf)) - op["errors"] = dict(zip(br.evol_basis, errors)) + if not qed: + evol_basis = br.evol_basis + else: + evol_basis = br.unified_evol_basis + op["pdfs"] = dict(zip(evol_basis, pdf)) + op["errors"] = dict(zip(evol_basis, errors)) # rotate/interpolate to target grid if targetgrid is not None: diff --git a/src/ekomark/benchmark/runner.py b/src/ekomark/benchmark/runner.py index 93bf054c1..43cd7da10 100644 --- a/src/ekomark/benchmark/runner.py +++ b/src/ekomark/benchmark/runner.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -""" -Abstract layer for running the benchmarks -""" +"""Abstract layer for running the benchmarks.""" import functools import logging import os @@ -21,9 +19,7 @@ class Runner(BenchmarkRunner): - """ - EKO specialization of the banana runner. - """ + """EKO specialization of the banana runner.""" db_base_cls = db.Base rotate_to_evolution_basis = False @@ -35,15 +31,31 @@ def __init__(self): @staticmethod def load_ocards(session, ocard_updates): + """ + Load operator cards. + + Parameters + ---------- + session : sqlalchemy.session.Session + DB ORM session + updates : dict + modifiers + + Returns + ------- + cards : list(dict) + list of records + """ return operators.load(session, ocard_updates) @staticmethod def skip_pdfs(_theory): + """Specify PDFs to skip.""" return [] def run_me(self, theory, ocard, _pdf): """ - Run eko + Run eko. Parameters ---------- @@ -59,7 +71,6 @@ def run_me(self, theory, ocard, _pdf): out : dict DGLAP result """ - # activate logging logStdout = logging.StreamHandler(sys.stdout) logStdout.setLevel(logging.INFO) @@ -105,7 +116,11 @@ def run_me(self, theory, ocard, _pdf): out_copy = eko.output.Output.load_tar(path) change_lab = False if self.rotate_to_evolution_basis: - out_copy.to_evol(source=True, target=True) + qed = new_theory["order"][1] > 0 + if not qed: + out_copy.to_evol(source=True, target=True) + else: + out_copy.to_uni_evol(source=True, target=True) change_lab = True save_operators_to_pdf( @@ -122,6 +137,23 @@ def run_me(self, theory, ocard, _pdf): return out def run_external(self, theory, ocard, pdf): + """ + Run external library. + + Parameters + ---------- + theory : dict + theory card + ocard : dict + operator card + pdf : lhapdf_type + pdf + + Returns + ------- + out : dict + DGLAP result + """ # pylint:disable=import-error,import-outside-toplevel if self.external.lower() == "lha": from .external import LHA_utils @@ -168,6 +200,19 @@ def run_external(self, theory, ocard, pdf): ) def log(self, theory, _, pdf, me, ext): + """ + Return a proper log table. + + Parameters + ---------- + theory : dict + theory card + pdf : lhapdf_type + pdf + me : eko.output.Output + eko output object containing all informations + + """ # return a proper log table log_tabs = {} xgrid = ext["target_xgrid"] @@ -176,8 +221,12 @@ def log(self, theory, _, pdf, me, ext): # LHA NNLO VFNS needs a special treatment # Valence contains only u and d rotate_to_evolution = None + qed = theory["QED"] > 0 if self.rotate_to_evolution_basis: - rotate_to_evolution = br.rotate_flavor_to_evolution.copy() + if not qed: + rotate_to_evolution = br.rotate_flavor_to_evolution.copy() + else: + rotate_to_evolution = br.rotate_flavor_to_unified_evolution.copy() if ( self.external == "LHA" and theory["PTO"] == 2 @@ -190,11 +239,27 @@ def log(self, theory, _, pdf, me, ext): pdf, xgrid, flavor_rotation=rotate_to_evolution, + qed=qed, ) + + def rotate_ev_to_uni_ev(ref_pdf): + import numpy as np + + rotate_evolution_to_flavour = np.linalg.inv(br.rotate_flavor_to_evolution) + rotation = ( + br.rotate_flavor_to_unified_evolution @ rotate_evolution_to_flavour + ) + ref_pdf_tmp = np.array([ref_pdf[pid] for pid in br.evol_basis]) + new_ref_pdfs_tmp = rotation @ ref_pdf_tmp + return {br.unified_evol_basis[i]: new_ref_pdfs_tmp[i] for i in range(14)} + for q2 in q2s: log_tab = dfdict.DFdict() - ref_pdfs = ext["values"][q2] + if qed and self.rotate_to_evolution_basis: + ref_pdfs = rotate_ev_to_uni_ev(ext["values"][q2]) + else: + ref_pdfs = ext["values"][q2] res = pdf_grid[q2] my_pdfs = res["pdfs"] my_pdf_errs = res["errors"] From 38710eca52b0943b6d45f7beef644cd798405c0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Sun, 21 Aug 2022 13:08:41 +0200 Subject: [PATCH 103/312] Fix rotation_parameters --- src/eko/evolution_operator/flavors.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/eko/evolution_operator/flavors.py b/src/eko/evolution_operator/flavors.py index 6f2d7e438..6937c24d2 100644 --- a/src/eko/evolution_operator/flavors.py +++ b/src/eko/evolution_operator/flavors.py @@ -191,7 +191,7 @@ def rotate_matching(nf, qed=False, inverse=False): l[f"{qpm}.{oth}"] = b / den else: l[f"{tot}.{tot}"] = 1.0 - l[f"{tot}.{totdelta}"] = 0.0 + # l[f"{tot}.{totdelta}"] = 0.0 l[f"{tot}.{qpm}"] = 1.0 l[f"{totdelta}.{tot}"] = a l[f"{totdelta}.{totdelta}"] = b @@ -244,10 +244,10 @@ def rotation_parameters(nf): nd_l = (nf - 1) - nu_l nu_h = constants.uplike_flavors(nf) nd_h = nf - nu_h - a = nd_l / (nf - 1) * (nd_h / nu_h - 1) - b = (nd_h / nu_h * nd_l - nu_l) / nf + a = (nd_h / nu_h - nd_l) / (nf - 1) + b = nf / nu_h * nu_l / (nf - 1) if nf in [4, 6]: # heavy flavor is up-like - c = nd_l / nu_l + c = nd_h / nu_h d = nu_l / (nf - 1) e = nu_l / (nf - 1) elif nf in [3, 5]: # heavy flavor is down-like From b73fcb3b806c9ab963d36cc30d330787e5dc1ccf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Sun, 21 Aug 2022 16:28:00 +0200 Subject: [PATCH 104/312] Fix again rotation_parameters --- src/eko/evolution_operator/flavors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eko/evolution_operator/flavors.py b/src/eko/evolution_operator/flavors.py index 6937c24d2..ead40c118 100644 --- a/src/eko/evolution_operator/flavors.py +++ b/src/eko/evolution_operator/flavors.py @@ -244,7 +244,7 @@ def rotation_parameters(nf): nd_l = (nf - 1) - nu_l nu_h = constants.uplike_flavors(nf) nd_h = nf - nu_h - a = (nd_h / nu_h - nd_l) / (nf - 1) + a = (nd_h / nu_h * nu_l - nd_l) / (nf - 1) b = nf / nu_h * nu_l / (nf - 1) if nf in [4, 6]: # heavy flavor is up-like c = nd_h / nu_h From c46cd2cabbe2bced80bb4058d3b0018b279e4ec3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Sun, 21 Aug 2022 19:26:56 +0200 Subject: [PATCH 105/312] Add documentation about qed matching and basis rotation --- extras/qed_matching/Makefile | 9 ++ extras/qed_matching/matching_and_rotation.aux | 7 + extras/qed_matching/matching_and_rotation.pdf | Bin 0 -> 139253 bytes extras/qed_matching/matching_and_rotation.tex | 141 ++++++++++++++++++ 4 files changed, 157 insertions(+) create mode 100644 extras/qed_matching/Makefile create mode 100644 extras/qed_matching/matching_and_rotation.aux create mode 100644 extras/qed_matching/matching_and_rotation.pdf create mode 100644 extras/qed_matching/matching_and_rotation.tex diff --git a/extras/qed_matching/Makefile b/extras/qed_matching/Makefile new file mode 100644 index 000000000..3a2875840 --- /dev/null +++ b/extras/qed_matching/Makefile @@ -0,0 +1,9 @@ +all: matching_and_rotation.pdf + +%.pdf: %.tex + pdflatex $< + +clean: + rm -f *.aux + rm -f *.log + rm -f *.pdf diff --git a/extras/qed_matching/matching_and_rotation.aux b/extras/qed_matching/matching_and_rotation.aux new file mode 100644 index 000000000..73c5dc3a2 --- /dev/null +++ b/extras/qed_matching/matching_and_rotation.aux @@ -0,0 +1,7 @@ +\relax +\@writefile{toc}{\contentsline {section}{\numberline {1}Matching}{1}{}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {2}Basis Rotation}{2}{}\protected@file@percent } +\@writefile{toc}{\contentsline {subsection}{\numberline {2.1}$\Sigma $}{2}{}\protected@file@percent } +\@writefile{toc}{\contentsline {subsection}{\numberline {2.2}$\Sigma _{\Delta }$}{2}{}\protected@file@percent } +\@writefile{toc}{\contentsline {subsection}{\numberline {2.3}$T_i$}{2}{}\protected@file@percent } +\gdef \@abspage@last{2} diff --git a/extras/qed_matching/matching_and_rotation.pdf b/extras/qed_matching/matching_and_rotation.pdf new file mode 100644 index 0000000000000000000000000000000000000000..2e009f52f9622482aef69c5a6400fbd3911a7fa8 GIT binary patch literal 139253 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

J)yZaYdLf3#dICAASBfcLnFqg}B6;5!X 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¬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%0Dy<(H0TkET7gQvd`=Z5Xq*eV)-S1G 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 V^ZvPrf+Zw?5=2~F%Gz+E{{p-#{L}yd literal 0 HcmV?d00001 diff --git a/extras/qed_matching/matching_and_rotation.tex b/extras/qed_matching/matching_and_rotation.tex new file mode 100644 index 000000000..a14cb45a4 --- /dev/null +++ b/extras/qed_matching/matching_and_rotation.tex @@ -0,0 +1,141 @@ +\documentclass[a4paper,oneside]{article} +\usepackage[utf8]{inputenc} +\usepackage{xcolor} +\usepackage{amsmath} +\usepackage{amssymb} +\usepackage{amsfonts} + +%\usepackage[a4paper,top=3cm,bottom=3cm,left=3cm,right=3cm]{geometry} +\usepackage[a4paper,top=1.5cm,bottom=1.5cm,left=1.5cm,right=1.5cm]{geometry} + + +\title{Matching and Basis Rotation for the Intrinsic Unified Evolution Basis} +\author{Niccolò Laurenti} + +\date{} + +\begin{document} + +\maketitle + +In this document we will explain how the matching and the basis rotation are performed in the Intrinsic Unified Evolution Basis. + +\section{Matching} +Fist of all, we have to perform the matching between the basis vectors in the two different flavor schemes, i.e.\ the $n_f$ and the $n_f+1$ flavor schemes. The matching of the gluon, light quark and heavy quark PDFs are the followings: +\begin{align*} +g^{(n_f+1)}&=A^S_{gg}g^{(n_f)}+A^S_{gq}\Sigma^{(n_f)}_{(n_f)}+A^S_{gH}h^{(n_f)} \\ +l^{(n_f+1)} &= A^{ns}_{qq} l^{(n_f)} + {1\over2n_f} A^S_{qg}g^{(n_f)} \quad \text{and the same for $\bar{l}$}\\ +h^{(n_f+1)} &= {1\over2} A^{S}_{Hg} g^{(n_f)}_{(n_f)}+ {1\over2} A^{ps}_{Hq} \Sigma^{(n_f)}_{(n_f)} + A_{HH}h^{(n_f)} \quad \text{and the same for $\bar{h}$} +\end{align*} +From the second relation we get that +\begin{align*} +l^{(n_f+1)}_+ &= A^{ns}_{qq} l^{(n_f)}_+ + {1\over n_f} A^S_{qg}g^{(n_f)} \\ +l^{(n_f+1)}_- &= A^{ns}_{qq} l^{(n_f)}_- \\ +\Sigma^{(n_f+1)}_{(n_f)}&=A^S_{qg}g^{(n_f)}+A^{ns}_{qq}\Sigma^{(n_f)}_{(n_f)} \\ +V^{(n_f+1)}_{(n_f)}&=A^{ns}_{qq}V^{(n_f)}_{(n_f)} +\end{align*} +while from the third relation we get that +\begin{align*} +h^{(n_f+1)}_+ &= A^{S}_{Hg} g^{(n_f)}_{(n_f)}+A^{ps}_{Hq} \Sigma^{(n_f)}_{(n_f)} + A_{HH}h^{(n_f)}_+ \\ +h^{(n_f+1)}_- &= A_{HH}h^{(n_f)}_- +\end{align*} + The matching of the components $\Sigma_{\Delta(n_f)}$, $V_{\Delta(n_f)}$, $T_i$, $V_i$ are diagonal. For the $V$s this is trivial since they are composed by $l_-$. For $\Sigma_{\Delta(n_f)}$ instead: being it defined as + \begin{align*} + \Sigma^{(n_f+1)}_{\Delta(n_f)} & = \frac{n_d(n_f)}{n_u(n_f)} \sum_{i=1}^{n_u} u_{+\,i}^{(n_f+1)} - \sum_{i=1}^{n_d} d_{+\,i}^{(n_f+1)} + \end{align*} + the gluon contribution cancels, giving the relation + \begin{equation*} + \Sigma^{(n_f+1)}_{\Delta(n_f)}=A^{ns}_{qq}\Sigma^{(n_f+1)}_{\Delta(n_f)} +\end{equation*} +The same holds for the $T_i$ components. + +Observe that this holds up to NNLO, since that at N$^3$LO we have to consider also the pure singlet components of the light quark matching (I think that we have to add $\frac{1}{2n_f}A^{ps}_{qq}\Sigma^{nf}_{(nf)}$ to the matching of $l^{(n_f+1)}$, but I'm not 100\% sure. In this way the matching of $l_-$ remains diagonal, as it should be, and the same hold for $\Sigma_{\Delta(n_f)}$ and $T_i$). + +Up to second order the perturbative expansion of the matching terms is given by +\begin{align*} +A^S_{gg} & = 1+ a_s A^{S(1)}_{gg} + a_s^2 A^{S(2)}_{gg} \\ +A^S_{gq} & = 1+ a_s^2 A^{S(2)}_{gq} \\ +A^S_{gH} & = 1+ a_s A^{S(1)}_{gH} \\ +A^S_{qg} & = 1 \\ +A^{ns}_{qq} &= 1+ a_s^2 A^{ns(1)}_{qq} \\ +A^S_{Hg} & = 1+ a_s A^{S(1)}_{Hg} + a_s^2 A^{S(2)}_{Hg} \\ +A^{ps}_{Hq} & = 1+ a_s^2 A^{ps(2)}_{Hq} \\ +A_{HH} & = 1+ a_s A^{(2)}_{HH} +\end{align*} + +\section{Basis Rotation} + +After the matching we have to perform the basis rotation from the basis with $\Sigma_{(n_f)}^{(n_f+1)}$, $\Sigma_{\Delta(n_f)}^{(n_f+1)}$, $h_+^{(n_f+1)}$ to the basis with $\Sigma_{(n_f+1)}^{(n_f+1)}$, $\Sigma_{\Delta(n_f+1)}^{(n_f+1)}$, $T_i^{(n_f+1)}$ (all the considerations that we will do for this basis rotation apply identically to the components $V$, $V_\Delta$, $h_-$, $V_i$). Being all the PDFs in the $n_f+1$ flavor scheme, from now on we will drop the superscript $(n_f+1)$. + +\subsection{$\Sigma$} +For the $\Sigma$ component the basis rotation is very simple, being +\begin{equation*} +\Sigma_{(n_f+1)}=\Sigma_{(n_f)}+h_+ +\end{equation*} +\subsection{$\Sigma_{\Delta}$} +This component requires a bit more work: starting from +\begin{equation*} +\begin{cases} +\Sigma &= \Sigma_u+\Sigma_d \\ +\Sigma_{\Delta} &= \frac{n_d}{n_u}\Sigma_u-\Sigma_d +\end{cases} +\end{equation*} +we obtain that +\begin{equation*} +\begin{cases} +\Sigma_u &= \frac{n_u}{n_f}\Bigl(\Sigma+\Sigma_{\Delta} \Bigr)\\ +\Sigma_d &= \frac{n_d}{n_f}\Sigma-\frac{n_u}{n_f}\Sigma_{\Delta} +\end{cases} +\end{equation*} + +Therefore, we find +\begin{align*} +\Sigma_{\Delta(n_f+1)} &= \frac{n_u(n_f+1)}{n_d(n_f+1)}\Sigma_{u(n_f)} - \Sigma_{d(n_f)} + k(n_f) h_+ +\end{align*} +where +\begin{equation*} +k(n_f) = +\begin{cases} + \frac{n_u(n_f+1)}{n_d(n_f+1)} \quad \text{if h=up-like}\\ +-1 \quad \text{if h=down-like} +\end{cases} +\end{equation*} + +In the end we find that +\begin{align*} +\Sigma_{\Delta(n_f+1)} &= \Bigl(\frac{n_d(n_f+1)}{n_u(n_f+1)}n_u(n_f)-n_d(n_f)\Bigr)\Sigma_{(n_f)} + \frac{n_f+1}{n_u(n_f+1)}\frac{n_u(n_f)}{n_f}\Sigma_{\Delta(n_f)} + k(n_f) h_+ +\end{align*} + +\subsection{$T_i$} +In the end, we have to find the rotation for the $T_i$ component: being +\begin{align*} +T_3^d &=d^+ - s^+ = \Sigma_{d(n_f)} - h_+\quad \text{for $n_f=3$}\\ +T_3^u &=u^+ - c^+ =\Sigma_{u(n_f)} - h_+\quad \text{for $n_f=4$}\\ +T_8^d &=d^+ + s^+ - 2b^+ =\Sigma_{d(n_f)} - 2h_+\quad \text{for $n_f=5$}\\ +T_8^u &=u^+ + c^+ - 2t^+ =\Sigma_{u(n_f)} - 2h_+\quad \text{for $n_f=6$} +\end{align*} + +Using the expressions of $\Sigma_u$ and $\Sigma_d$ as a function of $\Sigma$ and $\Sigma_\Delta$, we can write that +\begin{equation*} +T_i = f_1(n_f) \Sigma_{(n_f)} + f_2(n_f)\Sigma_{\Delta(n_f)} + f_3(n_f)h_+ +\end{equation*} +with +\begin{align*} +f_1(n_f) &= +\begin{cases} +&\frac{n_u(n_f)}{n_f} \quad \text{if $h$ is up-like ($n_f$=3,5)} \\ +&\frac{n_d(n_f)}{n_f} \quad \text{if $h$ is down-like ($n_f$=2,4)} +\end{cases} \\ +f_2(n_f) &= +\begin{cases} +&\frac{n_u(n_f)}{n_f} \quad \text{if $h$ is up-like} \\ +&-\frac{n_u(n_f)}{n_f} \quad \text{if $h$ is down-like} +\end{cases} \\ +f_3(n_f) &= +\begin{cases} +&-1\quad \text{if $h$ is $s$, $c$ ($n_f$=2,3)} \\ +&-2 \quad \text{if $h$ is $b$, $t$ ($n_f$=4,5)} +\end{cases} +\end{align*} + +\end{document} From 0aefe6a118d2b8559aebe015e4eae28ae3a6ac52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Sun, 21 Aug 2022 20:01:52 +0200 Subject: [PATCH 106/312] Remove products of latex compilation --- extras/qed_matching/matching_and_rotation.aux | 7 ------- extras/qed_matching/matching_and_rotation.pdf | Bin 139253 -> 0 bytes 2 files changed, 7 deletions(-) delete mode 100644 extras/qed_matching/matching_and_rotation.aux delete mode 100644 extras/qed_matching/matching_and_rotation.pdf diff --git a/extras/qed_matching/matching_and_rotation.aux b/extras/qed_matching/matching_and_rotation.aux deleted file mode 100644 index 73c5dc3a2..000000000 --- a/extras/qed_matching/matching_and_rotation.aux +++ /dev/null @@ -1,7 +0,0 @@ -\relax -\@writefile{toc}{\contentsline {section}{\numberline {1}Matching}{1}{}\protected@file@percent } -\@writefile{toc}{\contentsline {section}{\numberline {2}Basis Rotation}{2}{}\protected@file@percent } -\@writefile{toc}{\contentsline {subsection}{\numberline {2.1}$\Sigma $}{2}{}\protected@file@percent } -\@writefile{toc}{\contentsline {subsection}{\numberline {2.2}$\Sigma _{\Delta }$}{2}{}\protected@file@percent } -\@writefile{toc}{\contentsline {subsection}{\numberline {2.3}$T_i$}{2}{}\protected@file@percent } -\gdef \@abspage@last{2} diff --git a/extras/qed_matching/matching_and_rotation.pdf b/extras/qed_matching/matching_and_rotation.pdf deleted file mode 100644 index 2e009f52f9622482aef69c5a6400fbd3911a7fa8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 139253 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

J)yZaYdLf3#dICAASBfcLnFqg}B6;5!X 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¬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%0Dy<(H0TkET7gQvd`=Z5Xq*eV)-S1G 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 V^ZvPrf+Zw?5=2~F%Gz+E{{p-#{L}yd From fa2c7edbd6e96545f95bc91e6ff811b6da8d0469 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 22 Aug 2022 15:39:02 +0200 Subject: [PATCH 107/312] Fix typo in matching_and_rotation.tex --- extras/qed_matching/matching_and_rotation.tex | 4 ++-- src/eko/basis_rotation.py | 4 ++-- src/eko/evolution_operator/flavors.py | 1 - src/eko/kernels/QEDnon_singlet.py | 15 +++++++++------ 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/extras/qed_matching/matching_and_rotation.tex b/extras/qed_matching/matching_and_rotation.tex index a14cb45a4..189ef8d53 100644 --- a/extras/qed_matching/matching_and_rotation.tex +++ b/extras/qed_matching/matching_and_rotation.tex @@ -90,13 +90,13 @@ \subsection{$\Sigma_{\Delta}$} Therefore, we find \begin{align*} -\Sigma_{\Delta(n_f+1)} &= \frac{n_u(n_f+1)}{n_d(n_f+1)}\Sigma_{u(n_f)} - \Sigma_{d(n_f)} + k(n_f) h_+ +\Sigma_{\Delta(n_f+1)} &= \frac{n_d(n_f+1)}{n_u(n_f+1)}\Sigma_{u(n_f)} - \Sigma_{d(n_f)} + k(n_f) h_+ \end{align*} where \begin{equation*} k(n_f) = \begin{cases} - \frac{n_u(n_f+1)}{n_d(n_f+1)} \quad \text{if h=up-like}\\ +\frac{n_d(n_f+1)}{n_u(n_f+1)} \quad \text{if h=up-like}\\ -1 \quad \text{if h=down-like} \end{cases} \end{equation*} diff --git a/src/eko/basis_rotation.py b/src/eko/basis_rotation.py index 538e5ca55..fc5b389d1 100644 --- a/src/eko/basis_rotation.py +++ b/src/eko/basis_rotation.py @@ -284,12 +284,12 @@ def ad_projector(ad_lab, nf, qed=False): proj = np.zeros_like(rotate_flavor_to_evolution, dtype=float) l = map_ad_to_evolution[ad_lab] basis = evol_basis - rotate = rotate_flavor_to_evolution.copy() # Maybe .copy() is useless + rotate = rotate_flavor_to_evolution.copy() else: proj = np.zeros_like(rotate_flavor_to_unified_evolution, dtype=float) l = map_ad_to_unified_evolution[ad_lab] basis = unified_evol_basis - rotate = rotate_flavor_to_unified_evolution + rotate = rotate_flavor_to_unified_evolution.copy() # restrict the evolution basis to light flavors # NOTE: the cut is only needed for "NS_p" and "NS_m", but the other lists # are 1-long so they are unaffected diff --git a/src/eko/evolution_operator/flavors.py b/src/eko/evolution_operator/flavors.py index ead40c118..90b0d2e5e 100644 --- a/src/eko/evolution_operator/flavors.py +++ b/src/eko/evolution_operator/flavors.py @@ -176,7 +176,6 @@ def rotate_matching(nf, qed=False, inverse=False): ("S", "Sdelta", f"T{names[nf]}", f"{q}+"), ("V", "Vdelta", f"V{names[nf]}", f"{q}-"), ): - # TODO : double check it a, b, c, d, e, f = rotation_parameters(nf) if inverse: den = -b * d + a * e - c * e + b * f diff --git a/src/eko/kernels/QEDnon_singlet.py b/src/eko/kernels/QEDnon_singlet.py index 5c5129412..1142a3a94 100644 --- a/src/eko/kernels/QEDnon_singlet.py +++ b/src/eko/kernels/QEDnon_singlet.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +"""Collection of QED non-singlet EKOs.""" import numba as nb import numpy as np @@ -10,7 +11,7 @@ @nb.njit(cache=True) def lo_aem1_exact(gamma_ns, a1, a0, aem, nf): # lo refers to the order in as1 """ - |LO| non-singlet exact EKO + |LO| non-singlet exact EKO. Parameters ---------- @@ -37,7 +38,7 @@ def lo_aem1_exact(gamma_ns, a1, a0, aem, nf): # lo refers to the order in as1 @nb.njit(cache=True) def lo_aem2_exact(gamma_ns, a1, a0, aem, nf): # lo refers to the order in as1 """ - |LO| non-singlet exact EKO + |LO| non-singlet exact EKO. Parameters ---------- @@ -64,7 +65,7 @@ def lo_aem2_exact(gamma_ns, a1, a0, aem, nf): # lo refers to the order in as1 @nb.njit(cache=True) def nlo_aem1_exact(gamma_ns, a1, a0, aem, nf): """ - |NLO| non-singlet exact EKO + |NLO| non-singlet exact EKO. Parameters ---------- @@ -92,7 +93,7 @@ def nlo_aem1_exact(gamma_ns, a1, a0, aem, nf): @nb.njit(cache=True) def nlo_aem2_exact(gamma_ns, a1, a0, aem, nf): """ - |NLO| non-singlet exact EKO + |NLO| non-singlet exact EKO. Parameters ---------- @@ -121,7 +122,7 @@ def nlo_aem2_exact(gamma_ns, a1, a0, aem, nf): @nb.njit(cache=True) def nnlo_aem1_exact(gamma_ns, a1, a0, aem, nf): """ - |NNLO| non-singlet exact EKO + |NNLO| non-singlet exact EKO. Parameters ---------- @@ -150,7 +151,7 @@ def nnlo_aem1_exact(gamma_ns, a1, a0, aem, nf): @nb.njit(cache=True) def nnlo_aem2_exact(gamma_ns, a1, a0, aem, nf): """ - |NNLO| non-singlet exact EKO + |NNLO| non-singlet exact EKO. Parameters ---------- @@ -213,6 +214,8 @@ def dispatcher( return non_singlet.dispatcher( order, method, gamma_ns[0], a1, a0, nf, ev_op_iterations ) + # this if is probably useless since when order[1] == 0 + # the code never enters in this module if order[1] == 1: if order[0] == 1: return lo_aem1_exact(gamma_ns, a1, a0, aem, nf) From c9b743a6948104f001ca62e74543c0be208abfac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 23 Aug 2022 11:56:49 +0200 Subject: [PATCH 108/312] Fix typos in as1aem1.py --- src/eko/anomalous_dimensions/as1aem1.py | 63 ++++++++++++++++++------- 1 file changed, 45 insertions(+), 18 deletions(-) diff --git a/src/eko/anomalous_dimensions/as1aem1.py b/src/eko/anomalous_dimensions/as1aem1.py index bdd35213e..b02bfa388 100644 --- a/src/eko/anomalous_dimensions/as1aem1.py +++ b/src/eko/anomalous_dimensions/as1aem1.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -""" -This file contains the O(as1aem1) Altarelli-Parisi splitting kernels. -""" +"""Contains the O(as1aem1) Altarelli-Parisi splitting kernels.""" import numba as nb import numpy as np @@ -12,7 +10,7 @@ @nb.njit(cache=True) def gamma_phq(N, sx): - """Computes the O(as1aem1) photon-quark anomalous dimension + r"""Compute the O(as1aem1) photon-quark anomalous dimension. Implements Eq. (36) of :cite:`deFlorian:2015ujt`. @@ -49,7 +47,7 @@ def gamma_phq(N, sx): @nb.njit(cache=True) def gamma_qph(N, nf, sx): - """Computes the O(as1aem1) quark-photon anomalous dimension + r"""Compute the O(as1aem1) quark-photon anomalous dimension. Implements Eq. (26) of :cite:`deFlorian:2015ujt`. @@ -97,7 +95,7 @@ def gamma_qph(N, nf, sx): @nb.njit(cache=True) def gamma_gph(N): - """Computes the O(as1aem1) gluon-photon anomalous dimension + r"""Compute the O(as1aem1) gluon-photon anomalous dimension. Implements Eq. (27) of :cite:`deFlorian:2015ujt`. @@ -122,7 +120,7 @@ def gamma_gph(N): @nb.njit(cache=True) def gamma_phg(N): - """Computes the O(as1aem1) photon-gluon anomalous dimension + r"""Compute the O(as1aem1) photon-gluon anomalous dimension. Implements Eq. (30) of :cite:`deFlorian:2015ujt`. @@ -142,7 +140,7 @@ def gamma_phg(N): @nb.njit(cache=True) def gamma_qg(N, nf, sx): - """Computes the O(as1aem1) quark-gluon singlet anomalous dimension. + r"""Compute the O(as1aem1) quark-gluon singlet anomalous dimension. Implements Eq. (29) of :cite:`deFlorian:2015ujt`. @@ -167,7 +165,7 @@ def gamma_qg(N, nf, sx): @nb.njit(cache=True) def gamma_gq(N, sx): - """Computes the O(as1aem1) gluon-quark singlet anomalous dimension. + r"""Compute the O(as1aem1) gluon-quark singlet anomalous dimension. Implements Eq. (35) of :cite:`deFlorian:2015ujt`. @@ -190,7 +188,7 @@ def gamma_gq(N, sx): @nb.njit(cache=True) def gamma_phph(nf): - """Computes the O(as1aem1) photon-photon singlet anomalous dimension. + r"""Compute the O(as1aem1) photon-photon singlet anomalous dimension. Implements Eq. (28) of :cite:`deFlorian:2015ujt`. @@ -213,13 +211,10 @@ def gamma_phph(nf): @nb.njit(cache=True) def gamma_gg(): - """Computes the O(as1aem1) gluon-gluon singlet anomalous dimension. + r"""Compute the O(as1aem1) gluon-gluon singlet anomalous dimension. Implements Eq. (31) of :cite:`deFlorian:2015ujt`. - Parameters - ---------- - Returns ------- gamma_gg : complex @@ -232,7 +227,7 @@ def gamma_gg(): @nb.njit(cache=True) def gamma_nsp(N, sx): - """Computes the O(as1aem1) singlet-like non-singlet anomalous dimension. + r"""Compute the O(as1aem1) singlet-like non-singlet anomalous dimension. Implements sum of Eqs. (33-34) of :cite:`deFlorian:2015ujt`. @@ -295,7 +290,7 @@ def gamma_nsp(N, sx): @nb.njit(cache=True) def gamma_nsm(N, sx): - """Computes the O(as1aem1) valence-like non-singlet anomalous dimension. + r"""Compute the O(as1aem1) valence-like non-singlet anomalous dimension. Implements difference between Eqs. (33-34) of :cite:`deFlorian:2015ujt`. @@ -360,6 +355,22 @@ def gamma_nsm(N, sx): @nb.njit(cache=True) def gamma_singlet(N, nf, sx): + r"""Compute the O(as1aem1) singlet sector. + + Parameters + ---------- + N : complex + Mellin moment + nf : int + Number of active flavors + sx : np array + List of harmonic sums + + Returns + ------- + gamma_singlet : numpy.ndarray + O(as1aem1) singlet anomalous dimension :math:`\\gamma_{S}^{(1,1)}(N,nf,sx)` + """ e2avg = constants.e2avg(nf) e2delta = constants.vde2m(nf) - constants.vue2m(nf) + constants.e2avg(nf) e2_tot = nf * e2avg @@ -370,7 +381,7 @@ def gamma_singlet(N, nf, sx): [ e2_tot * gamma_gg(), e2_tot * gamma_gph(N), - e2_tot * gamma_gq(N, sx), + e2avg * gamma_gq(N, sx), vue2m * gamma_gq(N, sx), ], [ @@ -386,7 +397,7 @@ def gamma_singlet(N, nf, sx): vue2m * gamma_nsp(N, sx), ], [ - vde2m * gamma_gq(N, sx), + vde2m * gamma_qg(N, nf, sx), vde2m * gamma_qph(N, nf, sx), vde2m * gamma_nsp(N, sx), e2delta * gamma_nsp(N, sx), @@ -399,6 +410,22 @@ def gamma_singlet(N, nf, sx): @nb.njit(cache=True) def gamma_valence(N, nf, sx): + r"""Compute the O(as1aem1) valence sector. + + Parameters + ---------- + N : complex + Mellin moment + nf : int + Number of active flavors + sx : np array + List of harmonic sums + + Returns + ------- + gamma_singlet : numpy.ndarray + O(as1aem1) valence anomalous dimension :math:`\\gamma_{V}^{(1,1)}(N,nf,sx)` + """ e2avg = constants.e2avg(nf) e2delta = constants.vde2m(nf) - constants.vue2m(nf) + constants.e2avg(nf) vue2m = constants.vue2m(nf) From 9269551f76e467237c2a726cb3d82ee4aef310ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 23 Aug 2022 12:32:24 +0200 Subject: [PATCH 109/312] Fix typo in anomalous_dimensions/aem2.py --- src/eko/anomalous_dimensions/aem2.py | 65 +++++++++++++++++++------ src/eko/anomalous_dimensions/as1aem1.py | 2 +- 2 files changed, 50 insertions(+), 17 deletions(-) diff --git a/src/eko/anomalous_dimensions/aem2.py b/src/eko/anomalous_dimensions/aem2.py index ce129603f..357bdc515 100644 --- a/src/eko/anomalous_dimensions/aem2.py +++ b/src/eko/anomalous_dimensions/aem2.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -""" -This file contains the O(aem2) Altarelli-Parisi splitting kernels. -""" +"""Contains the O(aem2) Altarelli-Parisi splitting kernels.""" import numba as nb import numpy as np @@ -12,12 +10,16 @@ @nb.njit(cache=True) def gamma_phph(N, nf): - """Computes the O(aem2) photon-photon singlet anomalous dimension. + r"""Compute the O(aem2) photon-photon singlet anomalous dimension. Implements Eq. (68) of :cite:`deFlorian:2016gvk`. Parameters ---------- + N : complex + Mellin moment + nf : int + Number of active flavors Returns ------- @@ -26,7 +28,6 @@ def gamma_phph(N, nf): :math:`\\gamma_{\\gamma \\gamma}^{(0,2)}(N)` """ - nu = constants.uplike_flavors(nf) nd = nf - nu return ( @@ -38,7 +39,7 @@ def gamma_phph(N, nf): @nb.njit(cache=True) def gamma_uph(N, nf, sx): - """Computes the O(aem2) quark-photon anomalous dimension for up quarks. + r"""Compute the O(aem2) quark-photon anomalous dimension for up quarks. Implements Eq. (55) of :cite:`deFlorian:2016gvk` for q=u. @@ -62,7 +63,7 @@ def gamma_uph(N, nf, sx): @nb.njit(cache=True) def gamma_dph(N, nf, sx): - """Computes the O(aem2) quark-photon anomalous dimension for down quarks. + r"""Compute the O(aem2) quark-photon anomalous dimension for down quarks. Implements Eq. (55) of :cite:`deFlorian:2016gvk` for q=d. @@ -86,7 +87,7 @@ def gamma_dph(N, nf, sx): @nb.njit(cache=True) def gamma_phu(N, nf, sx): - """Computes the O(aem2) photon-quark anomalous dimension for up quarks. + r"""Compute the O(aem2) photon-quark anomalous dimension for up quarks. Implements Eq. (56) of :cite:`deFlorian:2016gvk` for q=u. @@ -119,7 +120,7 @@ def gamma_phu(N, nf, sx): @nb.njit(cache=True) def gamma_phd(N, nf, sx): - """Computes the O(aem2) photon-quark anomalous dimension for down quarks. + r"""Compute the O(aem2) photon-quark anomalous dimension for down quarks. Implements Eq. (56) of :cite:`deFlorian:2016gvk` for q=d. @@ -152,7 +153,7 @@ def gamma_phd(N, nf, sx): @nb.njit(cache=True) def gamma_nspu(N, nf, sx): - """Computes the O(aem2) singlet-like non-singlet anomalous dimension for up quarks. + r"""Compute the O(aem2) singlet-like non-singlet anomalous dimension for up quarks. Implements sum of Eqs. (57-58) of :cite:`deFlorian:2016gvk` for q=u. @@ -189,7 +190,7 @@ def gamma_nspu(N, nf, sx): @nb.njit(cache=True) def gamma_nspd(N, nf, sx): - """Computes the O(aem2) singlet-like non-singlet anomalous dimension for down quarks. + r"""Compute the O(aem2) singlet-like non-singlet anomalous dimension for down quarks. Implements sum of Eqs. (57-58) of :cite:`deFlorian:2016gvk` for q=d. @@ -226,7 +227,7 @@ def gamma_nspd(N, nf, sx): @nb.njit(cache=True) def gamma_nsmu(N, nf, sx): - """Computes the O(aem2) valence-like non-singlet anomalous dimension for up quarks. + r"""Compute the O(aem2) valence-like non-singlet anomalous dimension for up quarks. Implements difference between Eqs. (57-58) of :cite:`deFlorian:2016gvk` for q=u. @@ -263,7 +264,7 @@ def gamma_nsmu(N, nf, sx): @nb.njit(cache=True) def gamma_nsmd(N, nf, sx): - """Computes the O(aem2) valence-like non-singlet anomalous dimension for down quarks. + r"""Compute the O(aem2) valence-like non-singlet anomalous dimension for down quarks. Implements difference between Eqs. (57-58) of :cite:`deFlorian:2016gvk` for q=d. @@ -300,7 +301,7 @@ def gamma_nsmd(N, nf, sx): @nb.njit(cache=True) def gamma_ps(N, nf): - """Computes the O(aem2) pure-singlet quark-quark anomalous dimension. + r"""Compute the O(aem2) pure-singlet quark-quark anomalous dimension. Implements Eq. (59) of :cite:`deFlorian:2016gvk`. @@ -329,6 +330,22 @@ def gamma_ps(N, nf): @nb.njit(cache=True) def gamma_singlet(N, nf, sx): + r"""Compute the O(aem2) singlet sector. + + Parameters + ---------- + N : complex + Mellin moment + nf : int + Number of active flavors + sx : np array + List of harmonic sums + + Returns + ------- + gamma_singlet : numpy.ndarray + O(aem2) singlet anomalous dimension :math:`\\gamma_{S}^{(0,2)}(N,nf,sx)` + """ nu = constants.uplike_flavors(nf) nd = nf - nu vu = nu / nf @@ -388,6 +405,22 @@ def gamma_singlet(N, nf, sx): @nb.njit(cache=True) def gamma_valence(N, nf, sx): + r"""Compute the O(aem2) valence sector. + + Parameters + ---------- + N : complex + Mellin moment + nf : int + Number of active flavors + sx : np array + List of harmonic sums + + Returns + ------- + gamma_singlet : numpy.ndarray + O(aem2) valence anomalous dimension :math:`\\gamma_{V}^{(0,2)}(N,nf,sx)` + """ nu = constants.uplike_flavors(nf) nd = nf - nu vu = nu / nf @@ -409,8 +442,8 @@ def gamma_valence(N, nf, sx): constants.eu2 * gamma_nsmu(N, nf, sx) - constants.ed2 * gamma_nsmd(N, nf, sx) ), - vu * constants.eu2 * gamma_nsmu(N, nf, sx) - + vd * constants.ed2 * gamma_nsmd(N, nf, sx), + vd * constants.eu2 * gamma_nsmu(N, nf, sx) + + vu * constants.ed2 * gamma_nsmd(N, nf, sx), ], ], np.complex_, diff --git a/src/eko/anomalous_dimensions/as1aem1.py b/src/eko/anomalous_dimensions/as1aem1.py index b02bfa388..5debfff27 100644 --- a/src/eko/anomalous_dimensions/as1aem1.py +++ b/src/eko/anomalous_dimensions/as1aem1.py @@ -427,9 +427,9 @@ def gamma_valence(N, nf, sx): O(as1aem1) valence anomalous dimension :math:`\\gamma_{V}^{(1,1)}(N,nf,sx)` """ e2avg = constants.e2avg(nf) - e2delta = constants.vde2m(nf) - constants.vue2m(nf) + constants.e2avg(nf) vue2m = constants.vue2m(nf) vde2m = constants.vde2m(nf) + e2delta = vde2m - vue2m + e2avg gamma_V_11 = np.array( [ [e2avg, vue2m], From 10d93ef2a6db33a31cdf5ad2a258b11d9ad1c94f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 24 Aug 2022 17:18:57 +0200 Subject: [PATCH 110/312] Add missing elements in ad_to_evol_map --- doc/source/theory/DGLAP.rst | 2 +- extras/qed_matching/matching_and_rotation.tex | 2 +- src/eko/evolution_operator/__init__.py | 70 ++++++------------- src/eko/evolution_operator/physical.py | 13 ++-- 4 files changed, 31 insertions(+), 56 deletions(-) diff --git a/doc/source/theory/DGLAP.rst b/doc/source/theory/DGLAP.rst index 1089df661..20d980e13 100644 --- a/doc/source/theory/DGLAP.rst +++ b/doc/source/theory/DGLAP.rst @@ -172,7 +172,7 @@ Here the strategies are: - for ``method in ['iterate-exact', 'iterate-expanded']`` we use a discretized path-ordering :cite:`Bonvini:2012sh`: .. math:: - \ESk{1}{a_s}{a_s^0} = \prod\limits_{k=n}^{0} \ESk{1}{a_s^{k+1}}{a_s^{k}}\quad \text{with} a_s^{n+1} = a_s + \ESk{1}{a_s}{a_s^0} = \prod\limits_{k=n}^{0} \ESk{1}{a_s^{k+1}}{a_s^{k}}\quad \text{with}\quad a_s^{n+1} = a_s where the order of the product is such that later |EKO| are to the left and diff --git a/extras/qed_matching/matching_and_rotation.tex b/extras/qed_matching/matching_and_rotation.tex index 189ef8d53..79528902c 100644 --- a/extras/qed_matching/matching_and_rotation.tex +++ b/extras/qed_matching/matching_and_rotation.tex @@ -45,7 +45,7 @@ \section{Matching} \end{align*} the gluon contribution cancels, giving the relation \begin{equation*} - \Sigma^{(n_f+1)}_{\Delta(n_f)}=A^{ns}_{qq}\Sigma^{(n_f+1)}_{\Delta(n_f)} + \Sigma^{(n_f+1)}_{\Delta(n_f)}=A^{ns}_{qq}\Sigma^{(n_f)}_{\Delta(n_f)} \end{equation*} The same holds for the $T_i$ components. diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 4abb6c6aa..737ad7a02 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- r""" -This module contains the central operator classes. +Contains the central operator classes. See :doc:`Operator overview `. """ @@ -32,7 +32,7 @@ @nb.njit(cache=True) def select_singlet_element(ker, mode0, mode1): """ - Select element of the singlet matrix + Select element of the singlet matrix. Parameters ---------- @@ -47,7 +47,6 @@ def select_singlet_element(ker, mode0, mode1): ker : complex singlet integration kernel element """ - k = 0 if mode0 == 100 else 1 l = 0 if mode1 == 100 else 1 return ker[k, l] @@ -56,7 +55,7 @@ def select_singlet_element(ker, mode0, mode1): @nb.njit(cache=True) def select_QEDsinglet_element(ker, mode0, mode1): """ - Select element of the singlet matrix + Select element of the singlet matrix. Parameters ---------- @@ -91,7 +90,7 @@ def select_QEDsinglet_element(ker, mode0, mode1): @nb.njit(cache=True) def select_QEDvalence_element(ker, mode0, mode1): """ - Select element of the singlet matrix + Select element of the singlet matrix. Parameters ---------- @@ -106,7 +105,6 @@ def select_QEDvalence_element(ker, mode0, mode1): ker : complex singlet integration kernel element """ - k = 0 if mode0 == 10200 else 1 l = 0 if mode1 == 10200 else 1 return ker[k, l] @@ -125,7 +123,7 @@ def select_QEDvalence_element(ker, mode0, mode1): @nb.experimental.jitclass(spec) class QuadKerBase: """ - Manage the common part of Mellin inversion integral + Manage the common part of Mellin inversion integral. Parameters ---------- @@ -141,20 +139,20 @@ class QuadKerBase: def __init__(self, u, is_log, logx, mode0): self.is_singlet = mode0 in [100, 21, 90] - self.is_QEDsinglet = mode0 in [100, 101, 21, 22] - self.is_QEDvalence = mode0 in [10200, 10204] + self.is_QEDsinglet = mode0 in [100, 101, 21, 22, 90] + self.is_QEDvalence = mode0 in [10200, 10204, 91] self.is_log = is_log self.u = u self.logx = logx @property def path(self): - """Returns the associated instance of :class:`eko.mellin.Path`""" + """Return the associated instance of :class:`eko.mellin.Path`.""" return mellin.Path(self.u, self.logx, self.is_singlet) @property def n(self): - """Returns the Mellin moment N""" + """Return the Mellin moment N.""" return self.path.n def integrand( @@ -162,7 +160,7 @@ def integrand( areas, ): """ - Get transformation to Mellin space integral + Get transformation to Mellin space integral. Parameters ---------- @@ -369,33 +367,6 @@ def quad_ker( return np.real(ker * integrand) -# @nb.njit(cache=True) -# def choose_matrix(order, mode0, ker_base, nf): -# if order[1] == 0: -# if ker_base.is_singlet: -# return ad.gamma_singlet(order, ker_base.n, nf) -# else: -# return ad.gamma_ns(order, mode0, ker_base.n, nf) -# else : -# if ker_base.is_QEDsinglet : -# return ad.gamma_singlet_qed(order, ker_base.n, nf) -# elif ker_base.is_QEDvalence: -# return ad.gamma_valence_qed(order, ker_base.n, nf) -# else: -# return ad.gamma_ns_qed(order, mode0, ker_base.n, nf) - -# @nb.njit(cache=True) -# def exponentiated_variation(order, gamma, nf, L): -# if order[1] == 0: -# gamma = sv.exponentiated.gamma_variation(gamma, order, nf, L) -# return gamma -# else : -# gamma[0][1:] = sv.exponentiated.gamma_variation( -# gamma[0][1:], order, nf, L -# ) -# return gamma - - class Operator(sv.ModeMixin): """Internal representation of a single EKO. @@ -440,6 +411,7 @@ def __init__( @property def n_pools(self): + """Return the number of integration cores.""" n_pools = self.config["n_integration_cores"] if n_pools > 0: return n_pools @@ -448,21 +420,21 @@ def n_pools(self): @property def fact_to_ren(self): - r"""Returns the factor :math:`(\mu_F/\mu_R)^2`""" + r"""Return the factor :math:`(\mu_F/\mu_R)^2`.""" return self.config["fact_to_ren"] @property def int_disp(self): - """Returns the interpolation dispatcher""" + """Return the interpolation dispatcher.""" return self.managers["interpol_dispatcher"] @property def grid_size(self): - """Returns the grid size""" + """Return the grid size.""" return self.int_disp.xgrid.size def mur2_shift(self, q2): - """Computes shifted renormalization scale. + """Compute shifted renormalization scale. Parameters ---------- @@ -480,7 +452,7 @@ def mur2_shift(self, q2): @property def a_s(self): - """Returns the computed values for :math:`a_s`""" + """Return the computed values for :math:`a_s`.""" sc = self.managers["strong_coupling"] a0 = sc.a_s( self.mur2_shift(self.q2_from), fact_scale=self.q2_from, nf_to=self.nf @@ -527,7 +499,7 @@ def labels(self): return labels def quad_ker(self, label, logx, areas): - """Partially initialized integrand function. + """Compute partially initialized integrand function. Parameters ---------- @@ -565,7 +537,7 @@ def quad_ker(self, label, logx, areas): ) def initialize_op_members(self): - """Init all ops with identity or zeros if we skip them""" + """Init all ops with identity or zeros if we skip them.""" eye = OpMember( np.eye(self.grid_size), np.zeros((self.grid_size, self.grid_size)) ) @@ -590,7 +562,7 @@ def run_op_integration( self, log_grid, ): - """Run the integration for each grid point + """Run the integration for each grid point. Parameters ---------- @@ -683,7 +655,7 @@ def compute(self): def integrate( self, ): - """Run the integration""" + """Run the integration.""" tot_start_time = time.perf_counter() # run integration in parallel for each grid point @@ -710,7 +682,7 @@ def integrate( ) def copy_ns_ops(self): - """Copy non-singlet kernels, if necessary""" + """Copy non-singlet kernels, if necessary.""" if self.order[1] == 0: if self.order[0] == 1: # in LO +=-=v for label in ["nsV", "ns-"]: diff --git a/src/eko/evolution_operator/physical.py b/src/eko/evolution_operator/physical.py index f44696377..84feb7368 100644 --- a/src/eko/evolution_operator/physical.py +++ b/src/eko/evolution_operator/physical.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +"""Contains the :class:`PhysicalOperator` class.""" import numpy as np from .. import basis_rotation as br @@ -8,7 +9,7 @@ class PhysicalOperator(member.OperatorBase): """ - This joins several fixed flavor scheme operators together. + Join several fixed flavor scheme operators together. - provides the connection between the 7-dimensional anomalous dimension basis and the 15-dimensional evolution basis @@ -27,8 +28,7 @@ class PhysicalOperator(member.OperatorBase): @classmethod def ad_to_evol_map(cls, op_members, nf, q2_final, intrinsic_range, qed=False): """ - Obtain map between the 3-dimensional anomalous dimension basis and the - 4-dimensional evolution basis. + Obtain map between the 3-dimensional anomalous dimension basis and the 4-dimensional evolution basis. .. todo:: in VFNS sometimes IC is irrelevant if nf>=4 @@ -84,6 +84,10 @@ def ad_to_evol_map(cls, op_members, nf, q2_final, intrinsic_range, qed=False): "ph.Sdelta": op_members[(22, 101)], "S.ph": op_members[(100, 22)], "S.Sdelta": op_members[(100, 101)], + "Sdelta.g": op_members[(101, 21)], + "Sdelta.ph": op_members[(101, 22)], + "Sdelta.S": op_members[(101, 100)], + "Sdelta.Sdelta": op_members[(101, 101)], "V.V": op_members[(10200, 10200)], "V.Vdelta": op_members[(10200, 10204)], "Vdelta.V": op_members[(10204, 10200)], @@ -119,8 +123,7 @@ def ad_to_evol_map(cls, op_members, nf, q2_final, intrinsic_range, qed=False): def to_flavor_basis_tensor(self, qed=False): """ - Convert the computations into an rank 4 tensor over flavor operator space and - momentum fraction operator space. + Convert the computations into an rank 4 tensor over flavor operator space and momentum fraction operator space. Returns ------- From 46ba7540d3f6c422a7f0211af57ac432d60a76ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 25 Aug 2022 16:15:34 +0200 Subject: [PATCH 111/312] Modify apfel_bench_qed.py --- benchmarks/apfel_bench_qed.py | 78 ++++++++++++++++++++++++++++------- 1 file changed, 63 insertions(+), 15 deletions(-) diff --git a/benchmarks/apfel_bench_qed.py b/benchmarks/apfel_bench_qed.py index c0fe997d0..9d8158b90 100644 --- a/benchmarks/apfel_bench_qed.py +++ b/benchmarks/apfel_bench_qed.py @@ -11,6 +11,13 @@ register(__file__) +base_theory = { + "Qref": 91.1870, + "mc": 1.3, + "mb": 4.75, + "mt": 172.0, +} + def tolist(input_dict): output_dict = input_dict.copy() @@ -40,19 +47,25 @@ def skip_pdfs(_theory): class BenchmarkFFNS(ApfelBenchmark): """Benckmark FFNS""" - ffns_theory = { - "FNS": "FFNS", - "ModEv": [ - "EXA", - # "EXP", - # "TRN", - ], - "NfFF": 5, - "kcThr": 0.0, - "kbThr": 0.0, - "ktThr": np.inf, - "Q0": 5.0, - } + ffns_theory = base_theory.copy() + + ffns_theory.update( + { + "FNS": "FFNS", + "ModEv": [ + "EXA", + # "EXP", + # "TRN", + ], + "NfFF": 5, + "kcThr": 0.0, + "kbThr": 0.0, + "ktThr": np.inf, + "Q0": 5.0, + "alphas": 0.118000, + "alphaqed": 0.007496, + } + ) ffns_theory = tolist(ffns_theory) def benchmark_plain(self, pto, qed): @@ -63,7 +76,42 @@ def benchmark_plain(self, pto, qed): self.run( cartesian_product(th), operators.build(operators.apfel_config), - ["CT14qed_proton"], + ["NNPDF23_nnlo_as_0118_qed"], + ) + + +class BenchmarkVFNS(ApfelBenchmark): + """Benckmark VFNS""" + + vfns_theory = base_theory.copy() + + vfns_theory.update( + { + "FNS": "VFNS", + "ModEv": [ + "EXA", + # "EXP", + # "TRN", + ], + "kcThr": 1.0, + "kbThr": 1.0, + "ktThr": np.inf, + "Q0": 5.0, + "alphas": 0.118000, + "alphaqed": 0.007496, + } + ) + ffns_theory = tolist(vfns_theory) + + def benchmark_plain(self, pto, qed): + """Plain configuration""" + + th = self.ffns_theory.copy() + th.update({"PTO": [pto], "QED": [qed]}) + self.run( + cartesian_product(th), + operators.build(operators.apfel_config), + ["NNPDF23_nnlo_as_0118_qed"], ) @@ -71,4 +119,4 @@ def benchmark_plain(self, pto, qed): obj = BenchmarkFFNS() - obj.benchmark_plain(1, 1) + obj.benchmark_plain(2, 1) From e694b42ea07d11e85711448b99d8096769f7c02f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 25 Aug 2022 16:33:13 +0200 Subject: [PATCH 112/312] Remove 91 from self.is_QEDvalence --- src/eko/evolution_operator/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 737ad7a02..1f07ceadd 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -140,7 +140,7 @@ class QuadKerBase: def __init__(self, u, is_log, logx, mode0): self.is_singlet = mode0 in [100, 21, 90] self.is_QEDsinglet = mode0 in [100, 101, 21, 22, 90] - self.is_QEDvalence = mode0 in [10200, 10204, 91] + self.is_QEDvalence = mode0 in [10200, 10204] self.is_log = is_log self.u = u self.logx = logx From 8a208cf8d4f3c471a6afe51b2321cc801fdec1da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 29 Aug 2022 16:38:20 +0200 Subject: [PATCH 113/312] Update PDF in apfel_bench_qed.py --- benchmarks/apfel_bench_qed.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/apfel_bench_qed.py b/benchmarks/apfel_bench_qed.py index 9d8158b90..8cd13557e 100644 --- a/benchmarks/apfel_bench_qed.py +++ b/benchmarks/apfel_bench_qed.py @@ -76,7 +76,7 @@ def benchmark_plain(self, pto, qed): self.run( cartesian_product(th), operators.build(operators.apfel_config), - ["NNPDF23_nnlo_as_0118_qed"], + ["NNPDF31_nnlo_as_0118_luxqed"], ) From 05b16b6e72f3372bf7487be16c794131eb7e8d40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 29 Aug 2022 16:41:55 +0200 Subject: [PATCH 114/312] Fix harmonics in anomalous_dimensions/__init__.py --- src/eko/anomalous_dimensions/__init__.py | 35 ++++++++++++++++-------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index a48c289ed..1eb93a592 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- r""" -This module contains the Altarelli-Parisi splitting kernels. +Contains the Altarelli-Parisi splitting kernels. Normalization is given by @@ -28,7 +28,7 @@ @nb.njit(cache=True) def exp_singlet(gamma_S): r""" - Computes the exponential and the eigensystem of the singlet anomalous dimension matrix + Compute the exponential and the eigensystem of the singlet anomalous dimension matrix. Parameters ---------- @@ -76,7 +76,7 @@ def exp_singlet(gamma_S): @nb.njit(cache=True) def exp_matrix(gamma_S): r""" - Computes the exponential and the eigensystem of the singlet anomalous dimension matrix + Compute the exponential and the eigensystem of the singlet anomalous dimension matrix. Parameters ---------- @@ -129,7 +129,7 @@ def exp_matrix(gamma_S): @nb.njit(cache=True) def gamma_ns(order, mode, n, nf): - r"""Computes the tower of the non-singlet anomalous dimensions + r"""Compute the tower of the non-singlet anomalous dimensions. Parameters ---------- @@ -209,7 +209,7 @@ def gamma_ns(order, mode, n, nf): @nb.njit(cache=True) def gamma_singlet(order, n, nf): - r"""Computes the tower of the singlet anomalous dimensions matrices + r"""Compute the tower of the singlet anomalous dimensions matrices. Parameters ---------- @@ -264,7 +264,7 @@ def gamma_singlet(order, n, nf): @nb.njit(cache=True) def gamma_ns_qed(order, mode, n, nf): r""" - Computes the tower of the non-singlet anomalous dimensions + Compute the tower of the non-singlet anomalous dimensions. Parameters ---------- @@ -293,7 +293,11 @@ def gamma_ns_qed(order, mode, n, nf): """ # cache the s-es max_weight = max(order) - sx = harmonics.sx(n, max_weight=max_weight + 1) + if max_weight >= 3: + # here we need only S1,S2,S3,S4 + sx = harmonics.sx(n, max_weight=max_weight + 1) + else: + sx = harmonics.sx(n, max_weight=max_weight) # now combine gamma_ns = np.zeros((order[0] + 1, order[1] + 1), np.complex_) if order[0] >= 1: @@ -344,7 +348,7 @@ def gamma_ns_qed(order, mode, n, nf): @nb.njit(cache=True) def gamma_singlet_qed(order, n, nf): r""" - Computes the tower of the singlet anomalous dimensions matrices + Compute the tower of the singlet anomalous dimensions matrices. Parameters ---------- @@ -368,7 +372,11 @@ def gamma_singlet_qed(order, n, nf): """ # cache the s-es max_weight = max(order) - sx = harmonics.sx(n, max_weight=max_weight + 1) + if max_weight >= 3: + # here we need only S1,S2,S3,S4 + sx = harmonics.sx(n, max_weight=max_weight + 1) + else: + sx = harmonics.sx(n, max_weight=max_weight) gamma_s = np.zeros((order[0] + 1, order[1] + 1, 4, 4), np.complex_) if order[0] >= 1: gamma_s[1, 0] = as1.gamma_QEDsinglet(n, sx[0], nf) @@ -381,7 +389,6 @@ def gamma_singlet_qed(order, n, nf): if order[1] >= 2: gamma_s[0, 2] = aem2.gamma_singlet(n, nf, sx) if order[0] == 3: - sx = np.append(sx, harmonics.S4(n)) gamma_s[3, 0] = as3.gamma_QEDsinglet(n, nf, sx) return gamma_s @@ -389,7 +396,7 @@ def gamma_singlet_qed(order, n, nf): @nb.njit(cache=True) def gamma_valence_qed(order, n, nf): r""" - Computes the tower of the singlet anomalous dimensions matrices + Compute the tower of the singlet anomalous dimensions matrices. Parameters ---------- @@ -413,7 +420,11 @@ def gamma_valence_qed(order, n, nf): """ # cache the s-es max_weight = max(order) - sx = harmonics.sx(n, max_weight=max_weight + 1) + if max_weight >= 3: + # here we need only S1,S2,S3,S4 + sx = harmonics.sx(n, max_weight=max_weight + 1) + else: + sx = harmonics.sx(n, max_weight=max_weight) gamma_v = np.zeros((order[0] + 1, order[1] + 1, 2, 2), np.complex_) if order[0] >= 1: gamma_v[1, 0] = as1.gamma_QEDvalence(n, sx[0]) From 30117bacfc5f7e597d7dcad5a52f827d5cdd04a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 29 Aug 2022 16:46:18 +0200 Subject: [PATCH 115/312] Fix basis rotation in physical --- src/eko/basis_rotation.py | 2 -- src/eko/evolution_operator/__init__.py | 15 +++++++++------ src/eko/evolution_operator/physical.py | 13 ------------- src/eko/kernels/QEDsinglet.py | 4 ++-- 4 files changed, 11 insertions(+), 23 deletions(-) diff --git a/src/eko/basis_rotation.py b/src/eko/basis_rotation.py index fc5b389d1..8a25a5fe6 100644 --- a/src/eko/basis_rotation.py +++ b/src/eko/basis_rotation.py @@ -3,8 +3,6 @@ import numpy as np -from . import constants - flavor_basis_pids = tuple([22] + list(range(-6, -1 + 1)) + [21] + list(range(1, 6 + 1))) r""" Sorted elements in Flavor Basis as |pid|. diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 1f07ceadd..3e994fb1b 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -70,19 +70,21 @@ def select_QEDsinglet_element(ker, mode0, mode1): ker : complex singlet integration kernel element """ - k = 0 - if mode0 == 22: + if mode0 == 21: + k = 0 + elif mode0 == 22: k = 1 elif mode0 == 100: k = 2 - elif mode0 == 101: + else: k = 3 - l = 0 - if mode1 == 22: + if mode1 == 21: + l = 0 + elif mode1 == 22: l = 1 elif mode1 == 100: l = 2 - elif mode1 == 101: + else: l = 3 return ker[k, l] @@ -139,6 +141,7 @@ class QuadKerBase: def __init__(self, u, is_log, logx, mode0): self.is_singlet = mode0 in [100, 21, 90] + # TODO : check 90 in is_QEDsinglet and 91 in is_QEDvalence self.is_QEDsinglet = mode0 in [100, 101, 21, 22, 90] self.is_QEDvalence = mode0 in [10200, 10204] self.is_log = is_log diff --git a/src/eko/evolution_operator/physical.py b/src/eko/evolution_operator/physical.py index 84feb7368..8a6337631 100644 --- a/src/eko/evolution_operator/physical.py +++ b/src/eko/evolution_operator/physical.py @@ -60,19 +60,6 @@ def ad_to_evol_map(cls, op_members, nf, q2_final, intrinsic_range, qed=False): n = f**2 - 1 m[f"V{n}.V{n}"] = op_members[(br.non_singlet_pids_map["ns-"], 0)] m[f"T{n}.T{n}"] = op_members[(br.non_singlet_pids_map["ns+"], 0)] - # deal with intrinsic heavy quark PDFs - if intrinsic_range is not None: - hqfl = "cbt" - op_id = member.OpMember.id_like( - op_members[(br.non_singlet_pids_map["nsV"], 0)] - ) - for intr_fl in intrinsic_range: - if intr_fl <= nf: # light quarks are not intrinsic - continue - hq = hqfl[intr_fl - 4] # find name - # intrinsic means no evolution, i.e. they are evolving with the identity - m[f"{hq}+.{hq}+"] = op_id.copy() - m[f"{hq}-.{hq}-"] = op_id.copy() else: m.update( { diff --git a/src/eko/kernels/QEDsinglet.py b/src/eko/kernels/QEDsinglet.py index c0579c861..be73fd5a7 100644 --- a/src/eko/kernels/QEDsinglet.py +++ b/src/eko/kernels/QEDsinglet.py @@ -1,16 +1,16 @@ # -*- coding: utf-8 -*- +"""Collection of QED singlet EKOs.""" import numba as nb import numpy as np from .. import anomalous_dimensions as ad from .. import beta from . import utils -from .singlet import lo_exact @nb.njit(cache=True) def eko_iterate(gamma_singlet, a1, a0, aem, nf, order, ev_op_iterations): - """Singlet QEDxQCD iterated (exact) EKO + """Singlet QEDxQCD iterated (exact) EKO. Parameters ---------- From 755661ba91206feff87e57234acb51a18d818a1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 1 Sep 2022 10:49:44 +0200 Subject: [PATCH 116/312] Fix orders in anomalous_dimensions/__init__.py --- src/eko/anomalous_dimensions/__init__.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index 1eb93a592..e85eb89cf 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -293,7 +293,11 @@ def gamma_ns_qed(order, mode, n, nf): """ # cache the s-es max_weight = max(order) - if max_weight >= 3: + if order == (1, 1): + sx = harmonics.sx(n, max_weight=3) + elif order[1] == 2 and order[0] <= 2: + sx = harmonics.sx(n, max_weight=3) + elif max_weight >= 3: # here we need only S1,S2,S3,S4 sx = harmonics.sx(n, max_weight=max_weight + 1) else: @@ -372,7 +376,11 @@ def gamma_singlet_qed(order, n, nf): """ # cache the s-es max_weight = max(order) - if max_weight >= 3: + if order == (1, 1): + sx = harmonics.sx(n, max_weight=3) + elif order[1] == 2 and order[0] <= 2: + sx = harmonics.sx(n, max_weight=3) + elif max_weight >= 3: # here we need only S1,S2,S3,S4 sx = harmonics.sx(n, max_weight=max_weight + 1) else: @@ -420,7 +428,11 @@ def gamma_valence_qed(order, n, nf): """ # cache the s-es max_weight = max(order) - if max_weight >= 3: + if order == (1, 1): + sx = harmonics.sx(n, max_weight=3) + elif order[1] == 2 and order[0] <= 2: + sx = harmonics.sx(n, max_weight=3) + elif max_weight >= 3: # here we need only S1,S2,S3,S4 sx = harmonics.sx(n, max_weight=max_weight + 1) else: From a7bf11a1488e45721fe5ec7c396fb390fd3a0221 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 1 Sep 2022 12:59:28 +0200 Subject: [PATCH 117/312] Fix calling of photon PDF from apfel --- benchmarks/apfel_bench_qed.py | 2 +- src/eko/evolution_operator/__init__.py | 2 +- src/ekomark/benchmark/external/apfel_utils.py | 11 +++++------ 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/benchmarks/apfel_bench_qed.py b/benchmarks/apfel_bench_qed.py index 8cd13557e..0aa858f39 100644 --- a/benchmarks/apfel_bench_qed.py +++ b/benchmarks/apfel_bench_qed.py @@ -119,4 +119,4 @@ def benchmark_plain(self, pto, qed): obj = BenchmarkFFNS() - obj.benchmark_plain(2, 1) + obj.benchmark_plain(2, 2) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 3e994fb1b..f5d6ca516 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -710,7 +710,7 @@ def copy_ns_ops(self): ].error = self.op_members[ (br.non_singlet_pids_map["ns-"], 0) ].error.copy() - elif self.order[1] == 1 and self.order[0] == 0: + elif self.order[1] == 1 and self.order[0] == 0: # TODO : double check it self.op_members[ (br.non_singlet_pids_map["ns-u"], 0) ].value = self.op_members[(br.non_singlet_pids_map["ns+u"], 0)].value.copy() diff --git a/src/ekomark/benchmark/external/apfel_utils.py b/src/ekomark/benchmark/external/apfel_utils.py index dbc94a9de..634c8a63a 100644 --- a/src/ekomark/benchmark/external/apfel_utils.py +++ b/src/ekomark/benchmark/external/apfel_utils.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -""" -APFEL interface -""" +"""APFEL interface.""" import time import numpy as np @@ -13,7 +11,6 @@ def compute_apfel_data( theory, operators, pdf, skip_pdfs, rotate_to_evolution_basis=False ): - """ Run APFEL to compute operators. @@ -35,7 +32,6 @@ def compute_apfel_data( ref : dict output containing: target_xgrid, values """ - target_xgrid = operators["interpolation_xgrid"] pdf_name = pdf.set().name @@ -90,7 +86,10 @@ def compute_apfel_data( # collect APFEL apf = [] for x in target_xgrid: - xf = apfel.xPDF(pid if pid != 21 else 0, x) + if pid != 22: + xf = apfel.xPDF(pid if pid != 21 else 0, x) + else: + xf = apfel.xgamma(x) # if pid == 4: # print(pid,x,xf) apf.append(xf) From b0f82eed95bb5431822fa9d2add552207a610d39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 1 Sep 2022 15:06:56 +0200 Subject: [PATCH 118/312] Add comments in copy_ns_ops --- src/eko/evolution_operator/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index f5d6ca516..d62885655 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -710,7 +710,7 @@ def copy_ns_ops(self): ].error = self.op_members[ (br.non_singlet_pids_map["ns-"], 0) ].error.copy() - elif self.order[1] == 1 and self.order[0] == 0: # TODO : double check it + elif self.order[1] == 1 and self.order[0] == 0: # at O(as0aem1) u-=u+, d-=d+ self.op_members[ (br.non_singlet_pids_map["ns-u"], 0) ].value = self.op_members[(br.non_singlet_pids_map["ns+u"], 0)].value.copy() @@ -723,3 +723,4 @@ def copy_ns_ops(self): self.op_members[ (br.non_singlet_pids_map["ns-d"], 0) ].error = self.op_members[(br.non_singlet_pids_map["ns+d"], 0)].error.copy() + # starting from O(as1aem1) P+ != P- From b876c2601a4f303d7da34e5e454688a9c1841e7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 1 Sep 2022 15:35:46 +0200 Subject: [PATCH 119/312] Add skip_singlet and skip_nonsingllet in QED --- benchmarks/apfel_bench_qed.py | 11 ++++++++++- src/eko/evolution_operator/__init__.py | 22 ++++++++++++++-------- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/benchmarks/apfel_bench_qed.py b/benchmarks/apfel_bench_qed.py index 0aa858f39..363854c60 100644 --- a/benchmarks/apfel_bench_qed.py +++ b/benchmarks/apfel_bench_qed.py @@ -6,6 +6,7 @@ from banana import register from banana.data import cartesian_product +from eko import interpolation from ekomark.benchmark.runner import Runner from ekomark.data import operators @@ -71,11 +72,19 @@ class BenchmarkFFNS(ApfelBenchmark): def benchmark_plain(self, pto, qed): """Plain configuration""" + oper_card = operators.build(operators.apfel_config) + oper_card.update( + { + "interpolation_xgrid": interpolation.make_lambert_grid(30), + "debug_skip_singlet": True, + } + ) + th = self.ffns_theory.copy() th.update({"PTO": [pto], "QED": [qed]}) self.run( cartesian_product(th), - operators.build(operators.apfel_config), + oper_card, ["NNPDF31_nnlo_as_0118_luxqed"], ) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index d62885655..f74b2016a 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -491,14 +491,20 @@ def labels(self): labels.extend(br.singlet_labels) else: # add +u and +d as default - labels.append(br.non_singlet_unified_labels[0]) - labels.append(br.non_singlet_unified_labels[2]) - # -u and -d become different starting from O(as1aem1) or O(aem2) - if self.order[1] >= 2 or self.order[0] >= 1: - labels.append(br.non_singlet_unified_labels[1]) - labels.append(br.non_singlet_unified_labels[3]) - labels.extend(br.valence_unified_labels) - labels.extend(br.singlet_unified_labels) + if self.config["debug_skip_non_singlet"]: + logger.warning("%s: skipping non-singlet sector", self.log_label) + else: + labels.append(br.non_singlet_unified_labels[0]) + labels.append(br.non_singlet_unified_labels[2]) + # -u and -d become different starting from O(as1aem1) or O(aem2) + if self.order[1] >= 2 or self.order[0] >= 1: + labels.append(br.non_singlet_unified_labels[1]) + labels.append(br.non_singlet_unified_labels[3]) + labels.extend(br.valence_unified_labels) + if self.config["debug_skip_singlet"]: + logger.warning("%s: skipping singlet sector", self.log_label) + else: + labels.extend(br.singlet_unified_labels) return labels def quad_ker(self, label, logx, areas): From c7f1438087200ed80907a65112cd0b7499295cc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 1 Sep 2022 15:37:43 +0200 Subject: [PATCH 120/312] Fix apfel_bench_qed --- benchmarks/apfel_bench_qed.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmarks/apfel_bench_qed.py b/benchmarks/apfel_bench_qed.py index 363854c60..dcb150dcd 100644 --- a/benchmarks/apfel_bench_qed.py +++ b/benchmarks/apfel_bench_qed.py @@ -72,7 +72,7 @@ class BenchmarkFFNS(ApfelBenchmark): def benchmark_plain(self, pto, qed): """Plain configuration""" - oper_card = operators.build(operators.apfel_config) + oper_card = operators.build(operators.apfel_config)[0] oper_card.update( { "interpolation_xgrid": interpolation.make_lambert_grid(30), @@ -84,7 +84,7 @@ def benchmark_plain(self, pto, qed): th.update({"PTO": [pto], "QED": [qed]}) self.run( cartesian_product(th), - oper_card, + list(oper_card), ["NNPDF31_nnlo_as_0118_luxqed"], ) From a14eeedbd428a8fbe4fa54db526ccba07256a6fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 2 Sep 2022 14:12:32 +0200 Subject: [PATCH 121/312] Add rotation to unified_evol_basis in apfel_utils, lhapdf_utils, pegasus_utils --- src/eko/anomalous_dimensions/as1aem1.py | 1 + src/eko/evolution_operator/__init__.py | 2 +- src/ekomark/benchmark/external/apfel_utils.py | 11 ++++++++-- .../benchmark/external/lhapdf_utils.py | 20 ++++++++++++------- .../benchmark/external/pegasus_utils.py | 15 +++++++++----- src/ekomark/benchmark/runner.py | 17 ++-------------- 6 files changed, 36 insertions(+), 30 deletions(-) diff --git a/src/eko/anomalous_dimensions/as1aem1.py b/src/eko/anomalous_dimensions/as1aem1.py index 5debfff27..940d03856 100644 --- a/src/eko/anomalous_dimensions/as1aem1.py +++ b/src/eko/anomalous_dimensions/as1aem1.py @@ -93,6 +93,7 @@ def gamma_qph(N, nf, sx): ) +# TODO : check the Nc factors in gg gph phg phph @nb.njit(cache=True) def gamma_gph(N): r"""Compute the O(as1aem1) gluon-photon anomalous dimension. diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index f74b2016a..622a788dd 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -490,10 +490,10 @@ def labels(self): else: labels.extend(br.singlet_labels) else: - # add +u and +d as default if self.config["debug_skip_non_singlet"]: logger.warning("%s: skipping non-singlet sector", self.log_label) else: + # add +u and +d as default labels.append(br.non_singlet_unified_labels[0]) labels.append(br.non_singlet_unified_labels[2]) # -u and -d become different starting from O(as1aem1) or O(aem2) diff --git a/src/ekomark/benchmark/external/apfel_utils.py b/src/ekomark/benchmark/external/apfel_utils.py index 634c8a63a..fd746c97e 100644 --- a/src/ekomark/benchmark/external/apfel_utils.py +++ b/src/ekomark/benchmark/external/apfel_utils.py @@ -97,14 +97,21 @@ def compute_apfel_data( # rotate if needed if rotate_to_evolution_basis: + qed = theory["QED"] > 0 + if not qed: + evol_basis = br.evol_basis + rotate_flavor_to_evolution = br.rotate_flavor_to_evolution + else: + evol_basis = br.unified_evol_basis + rotate_flavor_to_evolution = br.rotate_flavor_to_unified_evolution pdfs = np.array( [ tab[pid] if pid in tab else np.zeros(len(target_xgrid)) for pid in br.flavor_basis_pids ] ) - evol_pdf = br.rotate_flavor_to_evolution @ pdfs - tab = dict(zip(br.evol_basis, evol_pdf)) + evol_pdf = rotate_flavor_to_evolution @ pdfs + tab = dict(zip(evol_basis, evol_pdf)) apf_tabs[q2] = tab diff --git a/src/ekomark/benchmark/external/lhapdf_utils.py b/src/ekomark/benchmark/external/lhapdf_utils.py index c3533cda6..87723243f 100644 --- a/src/ekomark/benchmark/external/lhapdf_utils.py +++ b/src/ekomark/benchmark/external/lhapdf_utils.py @@ -1,13 +1,13 @@ # -*- coding: utf-8 -*- -""" -LHAPDF interface -""" +"""LHAPDF interface.""" import numpy as np from eko import basis_rotation as br -def compute_LHAPDF_data(operators, pdf, skip_pdfs, rotate_to_evolution_basis=False): +def compute_LHAPDF_data( + theory, operators, pdf, skip_pdfs, rotate_to_evolution_basis=False +): """ Run LHAPDF to compute operators. @@ -27,7 +27,6 @@ def compute_LHAPDF_data(operators, pdf, skip_pdfs, rotate_to_evolution_basis=Fal ref : dict output containing: target_xgrid, values """ - target_xgrid = operators["interpolation_xgrid"] out_tabs = {} @@ -48,14 +47,21 @@ def compute_LHAPDF_data(operators, pdf, skip_pdfs, rotate_to_evolution_basis=Fal # rotate if needed if rotate_to_evolution_basis: + qed = theory["QED"] > 0 + if not qed: + evol_basis = br.evol_basis + rotate_flavor_to_evolution = br.rotate_flavor_to_evolution + else: + evol_basis = br.unified_evol_basis + rotate_flavor_to_evolution = br.rotate_flavor_to_unified_evolution pdfs = np.array( [ tab[pid] if pid in tab else np.zeros(len(target_xgrid)) for pid in br.flavor_basis_pids ] ) - evol_pdf = br.rotate_flavor_to_evolution @ pdfs - tab = dict(zip(br.evol_basis, evol_pdf)) + evol_pdf = rotate_flavor_to_evolution @ pdfs + tab = dict(zip(evol_basis, evol_pdf)) out_tabs[q2] = tab diff --git a/src/ekomark/benchmark/external/pegasus_utils.py b/src/ekomark/benchmark/external/pegasus_utils.py index 07f145848..ad0d80cd6 100644 --- a/src/ekomark/benchmark/external/pegasus_utils.py +++ b/src/ekomark/benchmark/external/pegasus_utils.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -""" -Pegasus interface -""" +"""Pegasus interface.""" import numpy as np from eko import basis_rotation as br @@ -94,14 +92,21 @@ def compute_pegasus_data(theory, operators, skip_pdfs, rotate_to_evolution_basis # rotate if needed if rotate_to_evolution_basis: + qed = theory["QED"] > 0 + if not qed: + evol_basis = br.evol_basis + rotate_flavor_to_evolution = br.rotate_flavor_to_evolution + else: + evol_basis = br.unified_evol_basis + rotate_flavor_to_evolution = br.rotate_flavor_to_unified_evolution pdfs = np.array( [ tab[pid] if pid in tab else np.zeros(len(target_xgrid)) for pid in br.flavor_basis_pids ] ) - evol_pdf = br.rotate_flavor_to_evolution @ pdfs - tab = dict(zip(br.evol_basis, evol_pdf)) + evol_pdf = rotate_flavor_to_evolution @ pdfs + tab = dict(zip(evol_basis, evol_pdf)) out_tabs[q2] = tab diff --git a/src/ekomark/benchmark/runner.py b/src/ekomark/benchmark/runner.py index 43cd7da10..519517ef7 100644 --- a/src/ekomark/benchmark/runner.py +++ b/src/ekomark/benchmark/runner.py @@ -169,6 +169,7 @@ def run_external(self, theory, ocard, pdf): # here theory card is not needed return lhapdf_utils.compute_LHAPDF_data( + theory, ocard, pdf, self.skip_pdfs(theory), @@ -242,24 +243,10 @@ def log(self, theory, _, pdf, me, ext): qed=qed, ) - def rotate_ev_to_uni_ev(ref_pdf): - import numpy as np - - rotate_evolution_to_flavour = np.linalg.inv(br.rotate_flavor_to_evolution) - rotation = ( - br.rotate_flavor_to_unified_evolution @ rotate_evolution_to_flavour - ) - ref_pdf_tmp = np.array([ref_pdf[pid] for pid in br.evol_basis]) - new_ref_pdfs_tmp = rotation @ ref_pdf_tmp - return {br.unified_evol_basis[i]: new_ref_pdfs_tmp[i] for i in range(14)} - for q2 in q2s: log_tab = dfdict.DFdict() - if qed and self.rotate_to_evolution_basis: - ref_pdfs = rotate_ev_to_uni_ev(ext["values"][q2]) - else: - ref_pdfs = ext["values"][q2] + ref_pdfs = ext["values"][q2] res = pdf_grid[q2] my_pdfs = res["pdfs"] my_pdf_errs = res["errors"] From 0fed5a5ed157c79007319f41665bbc86ad261f7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 9 Sep 2022 16:21:25 +0200 Subject: [PATCH 122/312] Fix names in uni-dglap.tex --- extras/uni-dglap/uni-dglap.tex | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/extras/uni-dglap/uni-dglap.tex b/extras/uni-dglap/uni-dglap.tex index a5c82d077..51f807b31 100644 --- a/extras/uni-dglap/uni-dglap.tex +++ b/extras/uni-dglap/uni-dglap.tex @@ -28,9 +28,9 @@ g & \\ \gamma & \\ \Sigma &= \Sigma_u + \Sigma_d \\ -\Delta_\Sigma & = \frac{n_d}{n_u}\Sigma_u - \Sigma_d \\ +\Sigma_\Delta & = \frac{n_d}{n_u}\Sigma_u - \Sigma_d \\ V & = V_u + V_d \\ -\Delta_V & = \frac{n_d}{n_u}V_u - V_d \\ +V_\Delta & = \frac{n_d}{n_u}V_u - V_d \\ T_3^d &=d^+ - s^+ \\ V_3^d &=d^- - s^- \\ T_3^u &=u^+ - c^+ \\ @@ -57,7 +57,7 @@ g \\ \gamma \\ \Sigma \\ -\Delta_\Sigma +\Sigma_\Delta \end{pmatrix} = \begin{pmatrix} @@ -70,7 +70,7 @@ g \\ \gamma \\ \Sigma \\ -\Delta_\Sigma +\Sigma_\Delta \end{pmatrix} \end{equation*} with @@ -87,7 +87,7 @@ \mu^2\frac{d}{d\mu^2} \begin{pmatrix} V \\ -\Delta_V +V_\Delta \end{pmatrix} = \begin{pmatrix} @@ -96,7 +96,7 @@ \end{pmatrix} \begin{pmatrix} V \\ -\Delta_V +V_\Delta \end{pmatrix} \end{equation*} \item Decoupled sector: @@ -117,7 +117,7 @@ g \\ \gamma \\ \Sigma \\ -\Delta_\Sigma +\Sigma_\Delta \end{pmatrix} = \\ &\begin{pmatrix} @@ -130,7 +130,7 @@ g \\ \gamma \\ \Sigma \\ -\Delta_\Sigma +\Sigma_\Delta \end{pmatrix} \end{align*} @@ -139,7 +139,7 @@ \mu^2\frac{d}{d\mu^2} \begin{pmatrix} V \\ -\Delta_V +V_\Delta \end{pmatrix} = \begin{pmatrix} @@ -148,7 +148,7 @@ \end{pmatrix} \begin{pmatrix} V \\ -\Delta_V +V_\Delta \end{pmatrix} \end{equation*} \item Decoupled sector: From 8ad97c0fbd4b2ad74aaaa6c9addcf49ab08adc24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 12 Sep 2022 10:04:21 +0200 Subject: [PATCH 123/312] Revert apfel_bench_qed.py --- benchmarks/apfel_bench_qed.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/benchmarks/apfel_bench_qed.py b/benchmarks/apfel_bench_qed.py index dcb150dcd..ccb32382f 100644 --- a/benchmarks/apfel_bench_qed.py +++ b/benchmarks/apfel_bench_qed.py @@ -72,19 +72,11 @@ class BenchmarkFFNS(ApfelBenchmark): def benchmark_plain(self, pto, qed): """Plain configuration""" - oper_card = operators.build(operators.apfel_config)[0] - oper_card.update( - { - "interpolation_xgrid": interpolation.make_lambert_grid(30), - "debug_skip_singlet": True, - } - ) - th = self.ffns_theory.copy() th.update({"PTO": [pto], "QED": [qed]}) self.run( cartesian_product(th), - list(oper_card), + operators.build(operators.apfel_config), ["NNPDF31_nnlo_as_0118_luxqed"], ) From 5b91a9b448cc1fb9ed0564b08aa85bfe5e5bd9ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 12 Sep 2022 11:45:29 +0200 Subject: [PATCH 124/312] Init test_kernels_QED --- src/eko/kernels/QEDnon_singlet.py | 2 +- tests/eko/test_kernels_QEDns.py | 61 ++++++++++++++++++++ tests/eko/test_kernels_QEDsinglet.py | 84 ++++++++++++++++++++++++++++ tests/eko/test_kernels_QEDvalence.py | 82 +++++++++++++++++++++++++++ 4 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 tests/eko/test_kernels_QEDns.py create mode 100644 tests/eko/test_kernels_QEDsinglet.py create mode 100644 tests/eko/test_kernels_QEDvalence.py diff --git a/src/eko/kernels/QEDnon_singlet.py b/src/eko/kernels/QEDnon_singlet.py index 1142a3a94..f1eef25a3 100644 --- a/src/eko/kernels/QEDnon_singlet.py +++ b/src/eko/kernels/QEDnon_singlet.py @@ -212,7 +212,7 @@ def dispatcher( # use always exact in LO if order[1] == 0: return non_singlet.dispatcher( - order, method, gamma_ns[0], a1, a0, nf, ev_op_iterations + order, method, gamma_ns[:, 0], a1, a0, nf, ev_op_iterations ) # this if is probably useless since when order[1] == 0 # the code never enters in this module diff --git a/tests/eko/test_kernels_QEDns.py b/tests/eko/test_kernels_QEDns.py new file mode 100644 index 000000000..7df8bef62 --- /dev/null +++ b/tests/eko/test_kernels_QEDns.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +import warnings + +import numpy as np +import pytest + +from eko import anomalous_dimensions as ad +from eko import beta +from eko.kernels import QEDnon_singlet as ns + +methods = [ + # "iterate-expanded", + # "decompose-expanded", + # "perturbative-expanded", + # "truncated", + # "ordered-truncated", + "iterate-exact", + # "decompose-exact", + # "perturbative-exact", +] + + +def test_zero(): + """No evolution results in exp(0)""" + nf = 3 + ev_op_iterations = 2 + for qcd in range(1, 3): + for qed in range(0, 2): + order = (qcd, qed) + if order == (0, 0): + continue + gamma_ns = ( + np.random.rand(qcd + 1, qed + 1) + np.random.rand(qcd + 1, qed + 1) * 1j + ) + for method in methods: + np.testing.assert_allclose( + ns.dispatcher( + order, method, gamma_ns, 1.0, 1.0, 1.0, nf, ev_op_iterations + ), + 1.0, + ) + np.testing.assert_allclose( + ns.dispatcher( + order, + method, + np.zeros((qcd + 1, qed + 1), dtype=complex), + 2.0, + 1.0, + 1.0, + nf, + ev_op_iterations, + ), + 1.0, + ) + + +def test_error(): + with pytest.raises(NotImplementedError): + ns.dispatcher( + (4, 2), "iterate-exact", np.random.rand(4, 3), 0.2, 0.1, 0.01, 3, 10 + ) diff --git a/tests/eko/test_kernels_QEDsinglet.py b/tests/eko/test_kernels_QEDsinglet.py new file mode 100644 index 000000000..fd635977a --- /dev/null +++ b/tests/eko/test_kernels_QEDsinglet.py @@ -0,0 +1,84 @@ +# -*- coding: utf-8 -*- +import warnings + +import numpy as np +import pytest + +from eko import anomalous_dimensions as ad +from eko.kernels import QEDsinglet as s + +methods = [ + # "iterate-expanded", + # "decompose-expanded", + # "perturbative-expanded", + # "truncated", + # "ordered-truncated", + "iterate-exact", + # "decompose-exact", + # "perturbative-exact", +] + + +def test_zero_lo(monkeypatch): + """No evolution results in exp(0)""" + nf = 3 + ev_op_iterations = 2 + ev_op_max_order = (3, 0) + for qcd in range(0, 3): + for qed in range(0, 2): + order = (qcd, qed) + if order == (0, 0): + continue + gamma_s = ( + np.random.rand(qcd + 1, qed + 1, 4, 4) + + np.random.rand(qcd + 1, qed + 1, 4, 4) * 1j + ) + # monkeypatch.setattr( + # ad, + # "exp_singlet", + # lambda gamma_S: ( + # gamma_S, + # 1, + # 1, + # np.array([[1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]), + # np.array([[0, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]), + # np.array([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 0]]), + # np.array([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 1]]), + # ), + # ) + for method in methods: + np.testing.assert_allclose( + s.dispatcher( + order, + method, + gamma_s, + 1, + 1, + 1, + nf, + ev_op_iterations, + ev_op_max_order, + ), + np.eye(4), + ) + np.testing.assert_allclose( + s.dispatcher( + order, + method, + np.zeros((qcd + 1, qed + 1, 4, 4), dtype=complex), + 2, + 1, + 1, + nf, + ev_op_iterations, + ev_op_max_order, + ), + np.eye(4), + ) + + +def test_error(): + with pytest.raises(NotImplementedError): + s.dispatcher( + (3, 2), "AAA", np.random.rand(4, 3, 2, 2), 0.2, 0.1, 0.01, 3, 10, 10 + ) diff --git a/tests/eko/test_kernels_QEDvalence.py b/tests/eko/test_kernels_QEDvalence.py new file mode 100644 index 000000000..fc7177800 --- /dev/null +++ b/tests/eko/test_kernels_QEDvalence.py @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- +import warnings + +import numpy as np +import pytest + +from eko import anomalous_dimensions as ad +from eko.kernels import QEDvalence as val + +methods = [ + # "iterate-expanded", + # "decompose-expanded", + # "perturbative-expanded", + # "truncated", + # "ordered-truncated", + "iterate-exact", + # "decompose-exact", + # "perturbative-exact", +] + + +def test_zero_lo(monkeypatch): + """No evolution results in exp(0)""" + nf = 3 + ev_op_iterations = 2 + ev_op_max_order = (3, 0) + for qcd in range(0, 3): + for qed in range(0, 2): + order = (qcd, qed) + if order == (0, 0): + continue + gamma_v = ( + np.random.rand(qcd + 1, qed + 1, 2, 2) + + np.random.rand(qcd + 1, qed + 1, 2, 2) * 1j + ) + # monkeypatch.setattr( + # ad, + # "exp_matrix", + # lambda gamma_S: ( + # gamma_S, + # 1, + # 1, + # np.array([[1, 0], [0, 0]]), + # np.array([[0, 0], [0, 1]]), + # ), + # ) + for method in methods: + np.testing.assert_allclose( + val.dispatcher( + order, + method, + gamma_v, + 1, + 1, + 1, + nf, + ev_op_iterations, + ev_op_max_order, + ), + np.eye(2), + ) + np.testing.assert_allclose( + val.dispatcher( + order, + method, + np.zeros((qcd + 1, qed + 1, 2, 2), dtype=complex), + 2, + 1, + 1, + nf, + ev_op_iterations, + ev_op_max_order, + ), + np.eye(2), + ) + + +def test_error(): + with pytest.raises(NotImplementedError): + val.dispatcher( + (3, 2), "AAA", np.random.rand(4, 3, 2, 2), 0.2, 0.1, 0.01, 3, 10, 10 + ) From 70b82c7e6c8210f6c28ff36cd5d6f0b7484a4d80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 12 Sep 2022 12:41:14 +0200 Subject: [PATCH 125/312] Write tests for basis rotation --- src/eko/evolution_operator/flavors.py | 2 +- tests/eko/test_basis_rotation.py | 24 ++++++++++ tests/eko/test_ev_op_flavors.py | 65 +++++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 1 deletion(-) diff --git a/src/eko/evolution_operator/flavors.py b/src/eko/evolution_operator/flavors.py index 90b0d2e5e..e56361817 100644 --- a/src/eko/evolution_operator/flavors.py +++ b/src/eko/evolution_operator/flavors.py @@ -179,7 +179,7 @@ def rotate_matching(nf, qed=False, inverse=False): a, b, c, d, e, f = rotation_parameters(nf) if inverse: den = -b * d + a * e - c * e + b * f - l[f"{tot}.{tot}"] = (c * e - b * f) / den + l[f"{tot}.{tot}"] = -(c * e - b * f) / den l[f"{tot}.{totdelta}"] = e / den l[f"{tot}.{oth}"] = -b / den l[f"{totdelta}.{tot}"] = (c * d - a * f) / den diff --git a/tests/eko/test_basis_rotation.py b/tests/eko/test_basis_rotation.py index faf320b73..b9d0c5da9 100644 --- a/tests/eko/test_basis_rotation.py +++ b/tests/eko/test_basis_rotation.py @@ -29,6 +29,30 @@ def test_ad_projector(): np.testing.assert_allclose(v3 @ ns_m, v3) +def test_ad_projector_qed(): + s = br.rotate_flavor_to_unified_evolution[2] + g = br.rotate_flavor_to_unified_evolution[0] + vd3 = br.rotate_flavor_to_unified_evolution[br.unified_evol_basis.index("Vd3")] + + s_to_s = br.ad_projector((100, 100), nf=6, qed=True) + # import pdb; pdb.set_trace() + np.testing.assert_allclose(s @ s_to_s, s) + np.testing.assert_allclose(g @ s_to_s, 0.0) + np.testing.assert_allclose(vd3 @ s_to_s, 0.0) + + g_to_s = br.ad_projector((21, 100), nf=6, qed=True) + + np.testing.assert_allclose(s @ g_to_s, 0.0) + np.testing.assert_allclose(g @ g_to_s, s) + np.testing.assert_allclose(vd3 @ g_to_s, 0.0) + + ns_md = br.ad_projector((br.non_singlet_pids_map["ns-d"], 0), nf=6, qed=True) + + np.testing.assert_allclose(s @ ns_md, 0.0, atol=1e-15) + np.testing.assert_allclose(g @ ns_md, 0.0) + np.testing.assert_allclose(vd3 @ ns_md, vd3) + + def test_ad_projectors(): for nf in range(3, 6 + 1): diag = np.array([0] * (1 + 6 - nf) + [1] * (1 + 2 * nf) + [0] * (6 - nf)) diff --git a/tests/eko/test_ev_op_flavors.py b/tests/eko/test_ev_op_flavors.py index 6debd5341..672f74baf 100644 --- a/tests/eko/test_ev_op_flavors.py +++ b/tests/eko/test_ev_op_flavors.py @@ -70,6 +70,33 @@ def test_get_range(): assert (4, 4) == flavors.get_range( [member.MemberName(n) for n in ["S.S", "V3.V3", "T15.T15"]] ) + assert (3, 3) == flavors.get_range( + [member.MemberName(n) for n in ["S.S", "Td3.Td3"]], True + ) + assert (3, 4) == flavors.get_range( + [member.MemberName(n) for n in ["S.S", "Td3.Td3", "Tu3.S"]], True + ) + assert (4, 4) == flavors.get_range( + [member.MemberName(n) for n in ["S.S", "Td3.Td3", "Tu3.Tu3"]], True + ) + assert (5, 5) == flavors.get_range( + [member.MemberName(n) for n in ["S.S", "Td3.Td3", "Tu3.Tu3", "Td8.Td8"]], True + ) + assert (6, 6) == flavors.get_range( + [ + member.MemberName(n) + for n in ["S.S", "Td3.Td3", "Tu3.Tu3", "Td8.Td8", "Tu8.Tu8"] + ], + True, + ) + with pytest.raises(ValueError): + flavors.get_range( + [ + member.MemberName(n) + for n in ["S.S", "Td3.Td3", "Tu3.Tu3", "Td8.Td8", "T35.T35"] + ], + True, + ) def test_rotate_pm_to_flavor(): @@ -92,6 +119,15 @@ def test_rotate_matching(): assert len(list(filter(lambda e: "b-" in e, m.keys()))) == 1 +def test_rotate_matching_qed(): + m = flavors.rotate_matching(4, True) + assert len(list(filter(lambda e: "c+" in e, m.keys()))) == 3 + assert len(list(filter(lambda e: "b-" in e, m.keys()))) == 1 + m = flavors.rotate_matching(5, True) + assert len(list(filter(lambda e: "b-" in e, m.keys()))) == 3 + assert len(list(filter(lambda e: "t+" in e, m.keys()))) == 1 + + def test_rotate_matching_is_inv(): def replace_names(k): for q in range(4, 6 + 1): @@ -114,6 +150,35 @@ def load(m): np.testing.assert_allclose(m @ minv, np.eye(len(br.evol_basis)), atol=1e-10) +def test_rotate_matching_is_inv_qed(): + def replace_names(k): + names = {3: "d3", 4: "u3", 5: "d8", 6: "u8"} + for q in range(4, 6 + 1): + k = k.replace(br.quark_names[q - 1] + "+", f"T{names[q]}").replace( + br.quark_names[q - 1] + "-", f"V{names[q]}" + ) + return k + + def load(m): + mm = np.zeros((len(br.unified_evol_basis), len(br.unified_evol_basis))) + for k, v in m.items(): + k = replace_names(k) + kk = k.split(".") + mm[ + br.unified_evol_basis.index(kk[0]), br.unified_evol_basis.index(kk[1]) + ] = v + return mm + + for nf in range(4, 6 + 1): + m = load(flavors.rotate_matching(nf, True)) + minv = load(flavors.rotate_matching_inverse(nf, True)) + # import pdb; pdb.set_trace() + print(m @ minv) + np.testing.assert_allclose( + m @ minv, np.eye(len(br.unified_evol_basis)), atol=1e-10 + ) + + def test_pids_from_intrinsic_unified_evol(): for nf in range(3, 6 + 1): labels = br.intrinsic_unified_evol_labels(nf) From f4ae911d641a15dfef9eea80ea3a4a45dafab688 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 12 Sep 2022 14:20:01 +0200 Subject: [PATCH 126/312] Test qed in ad_to_evol_map --- tests/eko/test_ev_op_physical.py | 62 ++++++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/tests/eko/test_ev_op_physical.py b/tests/eko/test_ev_op_physical.py index 98a7a18a5..e3907dc17 100644 --- a/tests/eko/test_ev_op_physical.py +++ b/tests/eko/test_ev_op_physical.py @@ -169,18 +169,22 @@ def test_to_flavor_basis_tensor_gg(self): np.testing.assert_allclose(vt[8:, :, 7, :], 0) -def mk_op_members(shape=(2, 2)): - m = np.random.rand(len(br.full_labels), *shape) - e = np.random.rand(len(br.full_labels), *shape) +def mk_op_members(shape=(2, 2), qed=False): + if not qed: + full_labels = br.full_labels + else: + full_labels = br.full_unified_labels + m = np.random.rand(len(full_labels), *shape) + e = np.random.rand(len(full_labels), *shape) om = {} - for j, l in enumerate(br.full_labels): + for j, l in enumerate(full_labels): om[l] = member.OpMember(m[j], e[j]) return om -def get_ad_to_evol_map(nf, intrinsic_range=None): - oms = mk_op_members() - m = PhysicalOperator.ad_to_evol_map(oms, nf, 1, intrinsic_range) +def get_ad_to_evol_map(nf, intrinsic_range=None, qed=False): + oms = mk_op_members(qed=qed) + m = PhysicalOperator.ad_to_evol_map(oms, nf, 1, intrinsic_range, qed) return sorted(map(str, m.op_members.keys())) @@ -203,3 +207,47 @@ def test_ad_to_evol_map(): assert sorted( [*triv_ops, "T15.T15", "V15.V15", "T24.T24", "V24.V24", "T35.T35", "V35.V35"] ) == get_ad_to_evol_map(6) + + +def test_ad_to_evol_map_qed(): + triv_ops = ( + "g.g", + "g.ph", + "g.S", + "g.Sdelta", + "ph.g", + "ph.ph", + "ph.S", + "ph.Sdelta", + "S.g", + "S.ph", + "S.S", + "S.Sdelta", + "Sdelta.g", + "Sdelta.ph", + "Sdelta.S", + "Sdelta.Sdelta", + "V.V", + "V.Vdelta", + "Vdelta.V", + "Vdelta.Vdelta", + "Vd3.Vd3", + "Td3.Td3", + ) + # nf=3 + assert sorted(triv_ops) == get_ad_to_evol_map(3, qed=True) + # nf=3 + IC + assert sorted([*triv_ops, "c+.c+", "c-.c-"]) == get_ad_to_evol_map(3, [4], qed=True) + # nf=3 + IC + IB + assert sorted( + [*triv_ops, "c+.c+", "c-.c-", "b+.b+", "b-.b-"] + ) == get_ad_to_evol_map(3, [4, 5], qed=True) + # nf=4 + IC(non-existant) + IB + ks = sorted([*triv_ops, "Vu3.Vu3", "Tu3.Tu3", "b+.b+", "b-.b-"]) + assert ks == get_ad_to_evol_map(4, [4, 5], qed=True) + # nf=4 + IB + assert ks == get_ad_to_evol_map(4, [5], qed=True) + # nf=6 + assert sorted( + [*triv_ops, "Tu3.Tu3", "Vu3.Vu3", "Td8.Td8", "Vd8.Vd8", "Tu8.Tu8", "Vu8.Vu8"] + ) == get_ad_to_evol_map(6, qed=True) From fa765f4d972ecc11c9121bac2bfc0953ec4e4b3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 12 Sep 2022 17:17:16 +0200 Subject: [PATCH 127/312] Test to_flavor_basis_tensor --- tests/eko/test_ev_op_physical.py | 41 ++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/tests/eko/test_ev_op_physical.py b/tests/eko/test_ev_op_physical.py index e3907dc17..836d168d8 100644 --- a/tests/eko/test_ev_op_physical.py +++ b/tests/eko/test_ev_op_physical.py @@ -168,6 +168,47 @@ def test_to_flavor_basis_tensor_gg(self): np.testing.assert_allclose(vt[7, :, :7, :], 0) np.testing.assert_allclose(vt[8:, :, 7, :], 0) + shape = (4, 4) + + def test_to_flavor_basis_tensor_ss_qed(self): + (SS,) = self._mkOM(1) + a = PhysicalOperator( + dict( + zip( + self._mkNames(("S.S",)), + (SS,), + ) + ), + 1, + ) + vt, _ = a.to_flavor_basis_tensor(qed=True) + np.testing.assert_allclose(vt[6, :, 6, :], vt[6, :, 5, :]) + np.testing.assert_allclose(vt[6, :, 6, :], vt[5, :, 6, :]) + np.testing.assert_allclose(vt[6, :, 6, :], SS.value[:, :] / (2 * 3)) + np.testing.assert_allclose(vt[1, :, :, :], 0) + np.testing.assert_allclose(vt[:, :, 1, :], 0) + np.testing.assert_allclose(vt[7, :, :, :], 0) + np.testing.assert_allclose(vt[:, :, 7, :], 0) + + def test_to_flavor_basis_tensor_gg_qed(self): + (gg,) = self._mkOM(1) + a = PhysicalOperator( + dict( + zip( + self._mkNames(("g.g",)), + (gg,), + ) + ), + 1, + ) + vt, _ = a.to_flavor_basis_tensor() + np.testing.assert_allclose(vt[6, :, 6, :], 0) + np.testing.assert_allclose(vt[7, :, 7, :], gg.value[:, :]) + np.testing.assert_allclose(vt[1, :, :, :], 0) + np.testing.assert_allclose(vt[:, :, 1, :], 0) + np.testing.assert_allclose(vt[7, :, :7, :], 0) + np.testing.assert_allclose(vt[8:, :, 7, :], 0) + def mk_op_members(shape=(2, 2), qed=False): if not qed: From b62666c3e087c5c188c36c5404680a580c8cf863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 12 Sep 2022 17:20:26 +0200 Subject: [PATCH 128/312] Test to_uni_evol --- tests/eko/test_output.py | 48 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/eko/test_output.py b/tests/eko/test_output.py index 6e7fc2617..cbb2339bf 100644 --- a/tests/eko/test_output.py +++ b/tests/eko/test_output.py @@ -284,3 +284,51 @@ def test_to_evol(self, fake_factory): o10["Q2grid"][q2_out]["operators"], o11["Q2grid"][q2_out]["operators"] ) chk_keys(o00, o10) + + def test_to_uni_evol(self, fake_factory): + interpolation_xgrid = np.array([0.5, 1.0]) + interpolation_polynomial_degree = 1 + interpolation_is_log = False + q2_ref = 1 + q2_out = 2 + Q2grid = fake_factory.mk_g( + [q2_out], len(br.flavor_basis_pids), len(interpolation_xgrid) + ) + d = dict( + interpolation_xgrid=interpolation_xgrid, + targetgrid=interpolation_xgrid, + inputgrid=interpolation_xgrid, + interpolation_polynomial_degree=interpolation_polynomial_degree, + interpolation_is_log=interpolation_is_log, + q2_ref=q2_ref, + inputpids=br.flavor_basis_pids, + targetpids=br.flavor_basis_pids, + Q2grid=Q2grid, + ) + o00 = output.Output(d) + o01 = copy.copy(o00) + o01.to_uni_evol() + o10 = copy.copy(o00) + o10.to_uni_evol(False, True) + o11 = copy.copy(o00) + o11.to_uni_evol(True, True) + chk_keys(o00, o11) + + # check the input rotated one + np.testing.assert_allclose(o01["inputpids"], br.unified_evol_basis_pids) + np.testing.assert_allclose(o01["targetpids"], br.flavor_basis_pids) + # rotate also target + o01.to_uni_evol(False, True) + np.testing.assert_allclose( + o01["Q2grid"][q2_out]["operators"], o11["Q2grid"][q2_out]["operators"] + ) + chk_keys(o00, o01) + # check the target rotated one + np.testing.assert_allclose(o10["inputpids"], br.flavor_basis_pids) + np.testing.assert_allclose(o10["targetpids"], br.unified_evol_basis_pids) + # rotate also input + o10.to_uni_evol() + np.testing.assert_allclose( + o10["Q2grid"][q2_out]["operators"], o11["Q2grid"][q2_out]["operators"] + ) + chk_keys(o00, o10) From 5fb4c6b6a07e5151814f743606a77337b7f1100a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 12 Sep 2022 18:31:21 +0200 Subject: [PATCH 129/312] Test matching conditions --- tests/eko/test_kernels_QEDns.py | 54 +++++++++++++++++ tests/eko/test_kernels_QEDsinglet.py | 2 +- tests/eko/test_matching.py | 89 ++++++++++++++++++++++++++++ 3 files changed, 144 insertions(+), 1 deletion(-) diff --git a/tests/eko/test_kernels_QEDns.py b/tests/eko/test_kernels_QEDns.py index 7df8bef62..bbff76381 100644 --- a/tests/eko/test_kernels_QEDns.py +++ b/tests/eko/test_kernels_QEDns.py @@ -54,6 +54,60 @@ def test_zero(): ) +# def test_ode(): +# aem = 0.01 +# nf = 3 +# ev_op_iterations = 10 +# delta_a = -1e-6 +# a0 = 0.3 +# betaQCD = np.zeros((4, 3), np.complex_) +# for i in range(4): +# betaQCD[i, 0] = beta.beta_qcd((i + 2, 0), nf) +# betaQCD[0, 1] = beta.beta_qcd((2, 1), nf) +# for qcd in range(1,3+1): +# for qed in range(1,2+1): +# order = (qcd, qed) +# for a1 in [0.1, 0.2]: +# gamma_ns = ( +# np.random.rand(qcd + 1, qed + 1) + np.random.rand(qcd + 1, qed + 1) * 1j +# ) +# gammatot = 0. +# betatot = 0. +# for i in range(0, order[0] + 1): +# for j in range(0, order[1] + 1): +# gammatot += gamma_ns[i, j] * a1**i * aem**j +# betatot += a1**2 * betaQCD[i, j] * a1**i * aem**j + +# r = gammatot / betatot +# for method in methods: +# rhs = r * ns.dispatcher( +# order, method, gamma_ns, a1, a0, aem, nf, ev_op_iterations +# ) +# lhs = ( +# ns.dispatcher( +# order, +# method, +# gamma_ns, +# a1 + 0.5 * delta_a, +# a0, +# aem, +# nf, +# ev_op_iterations, +# ) +# - ns.dispatcher( +# order, +# method, +# gamma_ns, +# a1 - 0.5 * delta_a, +# a0, +# aem, +# nf, +# ev_op_iterations, +# ) +# ) / delta_a +# np.testing.assert_allclose(lhs, rhs, atol=np.abs(delta_a)) + + def test_error(): with pytest.raises(NotImplementedError): ns.dispatcher( diff --git a/tests/eko/test_kernels_QEDsinglet.py b/tests/eko/test_kernels_QEDsinglet.py index fd635977a..24020ef3b 100644 --- a/tests/eko/test_kernels_QEDsinglet.py +++ b/tests/eko/test_kernels_QEDsinglet.py @@ -19,7 +19,7 @@ ] -def test_zero_lo(monkeypatch): +def test_zero(monkeypatch): """No evolution results in exp(0)""" nf = 3 ev_op_iterations = 2 diff --git a/tests/eko/test_matching.py b/tests/eko/test_matching.py index 7a1bf7608..81ecb6868 100644 --- a/tests/eko/test_matching.py +++ b/tests/eko/test_matching.py @@ -126,3 +126,92 @@ def test_split_ad_to_evol_map(self): d.op_members[member.MemberName("b+.g")].value, ome[(br.matching_hplus_pid, 21)].value, ) + + def test_split_ad_to_evol_map_qed(self): + ome = self.mkOME() + a = MatchingCondition.split_ad_to_evol_map(ome, 3, 1, [], qed=True) + triv_keys = [ + "S.S", + "S.g", + "g.S", + "g.g", + "Sdelta.Sdelta", + "V.V", + "Vdelta.Vdelta", + "Td3.Td3", + "Vd3.Vd3", + ] + # nf = 3 + keys3 = [ + "c+.S", + "c+.g", + # "c-.V", + ] + assert sorted(str(k) for k in a.op_members.keys()) == sorted( + [*triv_keys, *keys3] + ) + assert_almost_equal( + a.op_members[member.MemberName("V.V")].value, + ome[(200, 200)].value, + ) + # # if alpha is zero, nothing non-trivial should happen + b = MatchingCondition.split_ad_to_evol_map(ome, 3, 1, [], qed=True) + assert sorted(str(k) for k in b.op_members.keys()) == sorted( + [*triv_keys, *keys3] + ) + # assert_almost_equal( + # b.op_members[member.MemberName("V.V")].value, + # np.eye(self.shape[0]), + # ) + # nf=3 + IC + self.update_intrinsic_OME(ome) + c = MatchingCondition.split_ad_to_evol_map(ome, 3, 1, [4], qed=True) + assert sorted(str(k) for k in c.op_members.keys()) == sorted( + [*triv_keys, *keys3, "S.c+", "g.c+", "c+.c+", "c-.c-"] + ) + assert_almost_equal( + c.op_members[member.MemberName("V.V")].value, + b.op_members[member.MemberName("V.V")].value, + ) + # nf=3 + IB + d = MatchingCondition.split_ad_to_evol_map(ome, 3, 1, [5], qed=True) + assert sorted(str(k) for k in d.op_members.keys()) == sorted( + [*triv_keys, *keys3, "b+.b+", "b-.b-"] + ) + assert_almost_equal( + d.op_members[member.MemberName("b+.b+")].value, + np.eye(self.shape[0]), + ) + # nf=4 + IB + d = MatchingCondition.split_ad_to_evol_map(ome, 4, 1, [5], qed=True) + assert sorted(str(k) for k in d.op_members.keys()) == sorted( + [ + *triv_keys, + "Tu3.Tu3", + "Vu3.Vu3", + "S.b+", + "g.b+", + # "V.b-", + "b+.S", + "b+.g", + "b+.b+", + # "b-.V", + "b-.b-", + ] + ) + assert_almost_equal( + d.op_members[member.MemberName("V.V")].value, + a.op_members[member.MemberName("V.V")].value, + ) + # assert_almost_equal( + # d.op_members[member.MemberName("b-.V")].value, + # ome[(br.matching_hminus_pid, 200)].value, + # ) + assert_almost_equal( + d.op_members[member.MemberName("b+.S")].value, + ome[(br.matching_hplus_pid, 100)].value, + ) + assert_almost_equal( + d.op_members[member.MemberName("b+.g")].value, + ome[(br.matching_hplus_pid, 21)].value, + ) From f31a1386d9e268be3c703bb6de0a7859b13005c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 13 Sep 2022 11:00:53 +0200 Subject: [PATCH 130/312] Fix orders of hamonics in qed singlet and valence --- src/eko/anomalous_dimensions/__init__.py | 21 +++++---- src/eko/kernels/QEDnon_singlet.py | 2 +- tests/eko/test_kernels_QEDns.py | 11 +++-- tests/eko/test_kernels_QEDsinglet.py | 60 +++++++++++++++++++++++- tests/eko/test_kernels_QEDvalence.py | 60 ++++++++++++++++++++++-- 5 files changed, 133 insertions(+), 21 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index e85eb89cf..f96297a3b 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -376,13 +376,13 @@ def gamma_singlet_qed(order, n, nf): """ # cache the s-es max_weight = max(order) - if order == (1, 1): - sx = harmonics.sx(n, max_weight=3) - elif order[1] == 2 and order[0] <= 2: - sx = harmonics.sx(n, max_weight=3) - elif max_weight >= 3: + if max_weight >= 3: # here we need only S1,S2,S3,S4 sx = harmonics.sx(n, max_weight=max_weight + 1) + elif order[0] >= 1 and order[1] >= 1: + sx = harmonics.sx(n, max_weight=3) + elif order[1] == 2: + sx = harmonics.sx(n, max_weight=3) else: sx = harmonics.sx(n, max_weight=max_weight) gamma_s = np.zeros((order[0] + 1, order[1] + 1, 4, 4), np.complex_) @@ -428,13 +428,14 @@ def gamma_valence_qed(order, n, nf): """ # cache the s-es max_weight = max(order) - if order == (1, 1): - sx = harmonics.sx(n, max_weight=3) - elif order[1] == 2 and order[0] <= 2: - sx = harmonics.sx(n, max_weight=3) - elif max_weight >= 3: + max_weight = max(order) + if max_weight >= 3: # here we need only S1,S2,S3,S4 sx = harmonics.sx(n, max_weight=max_weight + 1) + elif order[0] >= 1 and order[1] >= 1: + sx = harmonics.sx(n, max_weight=3) + elif order[1] == 2: + sx = harmonics.sx(n, max_weight=3) else: sx = harmonics.sx(n, max_weight=max_weight) gamma_v = np.zeros((order[0] + 1, order[1] + 1, 2, 2), np.complex_) diff --git a/src/eko/kernels/QEDnon_singlet.py b/src/eko/kernels/QEDnon_singlet.py index f1eef25a3..5cf48ca47 100644 --- a/src/eko/kernels/QEDnon_singlet.py +++ b/src/eko/kernels/QEDnon_singlet.py @@ -212,7 +212,7 @@ def dispatcher( # use always exact in LO if order[1] == 0: return non_singlet.dispatcher( - order, method, gamma_ns[:, 0], a1, a0, nf, ev_op_iterations + order, method, gamma_ns[1:, 0], a1, a0, nf, ev_op_iterations ) # this if is probably useless since when order[1] == 0 # the code never enters in this module diff --git a/tests/eko/test_kernels_QEDns.py b/tests/eko/test_kernels_QEDns.py index bbff76381..416d0a94a 100644 --- a/tests/eko/test_kernels_QEDns.py +++ b/tests/eko/test_kernels_QEDns.py @@ -61,20 +61,21 @@ def test_zero(): # delta_a = -1e-6 # a0 = 0.3 # betaQCD = np.zeros((4, 3), np.complex_) -# for i in range(4): +# for i in range(3 + 1): # betaQCD[i, 0] = beta.beta_qcd((i + 2, 0), nf) -# betaQCD[0, 1] = beta.beta_qcd((2, 1), nf) -# for qcd in range(1,3+1): -# for qed in range(1,2+1): +# betaQCD[0, 1] = beta.beta_qcd((2, 1), nf) +# for qcd in range(1,1+1): +# for qed in range(0,0+1): # order = (qcd, qed) # for a1 in [0.1, 0.2]: # gamma_ns = ( # np.random.rand(qcd + 1, qed + 1) + np.random.rand(qcd + 1, qed + 1) * 1j # ) +# gamma_ns[0,0] = 0. # gammatot = 0. # betatot = 0. # for i in range(0, order[0] + 1): -# for j in range(0, order[1] + 1): +# for j in range(0, 0 + 1): # gammatot += gamma_ns[i, j] * a1**i * aem**j # betatot += a1**2 * betaQCD[i, j] * a1**i * aem**j diff --git a/tests/eko/test_kernels_QEDsinglet.py b/tests/eko/test_kernels_QEDsinglet.py index 24020ef3b..f8b5a27f6 100644 --- a/tests/eko/test_kernels_QEDsinglet.py +++ b/tests/eko/test_kernels_QEDsinglet.py @@ -24,8 +24,8 @@ def test_zero(monkeypatch): nf = 3 ev_op_iterations = 2 ev_op_max_order = (3, 0) - for qcd in range(0, 3): - for qed in range(0, 2): + for qcd in range(0, 3 + 1): + for qed in range(0, 2 + 1): order = (qcd, qed) if order == (0, 0): continue @@ -77,6 +77,62 @@ def test_zero(monkeypatch): ) +def test_zero_true_gamma(monkeypatch): + """No evolution results in exp(0)""" + nf = 3 + ev_op_iterations = 2 + ev_op_max_order = (3, 0) + for qcd in range(0, 3 + 1): + for qed in range(0, 2 + 1): + order = (qcd, qed) + if order == (0, 0): + continue + n = np.random.rand() + gamma_s = ad.gamma_singlet_qed(order, n, nf) + # monkeypatch.setattr( + # ad, + # "exp_singlet", + # lambda gamma_S: ( + # gamma_S, + # 1, + # 1, + # np.array([[1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]), + # np.array([[0, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]), + # np.array([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 0]]), + # np.array([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 1]]), + # ), + # ) + for method in methods: + np.testing.assert_allclose( + s.dispatcher( + order, + method, + gamma_s, + 1, + 1, + 1, + nf, + ev_op_iterations, + ev_op_max_order, + ), + np.eye(4), + ) + np.testing.assert_allclose( + s.dispatcher( + order, + method, + np.zeros((qcd + 1, qed + 1, 4, 4), dtype=complex), + 2, + 1, + 1, + nf, + ev_op_iterations, + ev_op_max_order, + ), + np.eye(4), + ) + + def test_error(): with pytest.raises(NotImplementedError): s.dispatcher( diff --git a/tests/eko/test_kernels_QEDvalence.py b/tests/eko/test_kernels_QEDvalence.py index fc7177800..897110f0d 100644 --- a/tests/eko/test_kernels_QEDvalence.py +++ b/tests/eko/test_kernels_QEDvalence.py @@ -19,13 +19,13 @@ ] -def test_zero_lo(monkeypatch): +def test_zero(monkeypatch): """No evolution results in exp(0)""" nf = 3 ev_op_iterations = 2 ev_op_max_order = (3, 0) - for qcd in range(0, 3): - for qed in range(0, 2): + for qcd in range(0, 3 + 1): + for qed in range(0, 2 + 1): order = (qcd, qed) if order == (0, 0): continue @@ -75,6 +75,60 @@ def test_zero_lo(monkeypatch): ) +def test_zero_true_gamma(monkeypatch): + """No evolution results in exp(0)""" + nf = 3 + ev_op_iterations = 2 + ev_op_max_order = (3, 0) + for qcd in range(0, 3 + 1): + for qed in range(0, 2 + 1): + order = (qcd, qed) + if order == (0, 0): + continue + n = np.random.rand() + gamma_v = ad.gamma_valence_qed(order, n, nf) + # monkeypatch.setattr( + # ad, + # "exp_matrix", + # lambda gamma_S: ( + # gamma_S, + # 1, + # 1, + # np.array([[1, 0], [0, 0]]), + # np.array([[0, 0], [0, 1]]), + # ), + # ) + for method in methods: + np.testing.assert_allclose( + val.dispatcher( + order, + method, + gamma_v, + 1, + 1, + 1, + nf, + ev_op_iterations, + ev_op_max_order, + ), + np.eye(2), + ) + np.testing.assert_allclose( + val.dispatcher( + order, + method, + np.zeros((qcd + 1, qed + 1, 2, 2), dtype=complex), + 2, + 1, + 1, + nf, + ev_op_iterations, + ev_op_max_order, + ), + np.eye(2), + ) + + def test_error(): with pytest.raises(NotImplementedError): val.dispatcher( From e1010c47fc17d6096ce84274d6de7584c3a37018 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 13 Sep 2022 11:12:00 +0200 Subject: [PATCH 131/312] Fix orders of hamonics in qed nonsinglet --- src/eko/anomalous_dimensions/__init__.py | 10 +++--- tests/eko/test_kernels_QEDns.py | 41 +++++++++++++++++++++--- 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index f96297a3b..b83ce1c46 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -293,13 +293,13 @@ def gamma_ns_qed(order, mode, n, nf): """ # cache the s-es max_weight = max(order) - if order == (1, 1): - sx = harmonics.sx(n, max_weight=3) - elif order[1] == 2 and order[0] <= 2: - sx = harmonics.sx(n, max_weight=3) - elif max_weight >= 3: + if max_weight >= 3: # here we need only S1,S2,S3,S4 sx = harmonics.sx(n, max_weight=max_weight + 1) + elif order[0] >= 1 and order[1] >= 1: + sx = harmonics.sx(n, max_weight=3) + elif order[1] == 2: + sx = harmonics.sx(n, max_weight=3) else: sx = harmonics.sx(n, max_weight=max_weight) # now combine diff --git a/tests/eko/test_kernels_QEDns.py b/tests/eko/test_kernels_QEDns.py index 416d0a94a..055e01c44 100644 --- a/tests/eko/test_kernels_QEDns.py +++ b/tests/eko/test_kernels_QEDns.py @@ -5,6 +5,7 @@ import pytest from eko import anomalous_dimensions as ad +from eko import basis_rotation as br from eko import beta from eko.kernels import QEDnon_singlet as ns @@ -24,11 +25,9 @@ def test_zero(): """No evolution results in exp(0)""" nf = 3 ev_op_iterations = 2 - for qcd in range(1, 3): - for qed in range(0, 2): + for qcd in range(1, 3 + 1): + for qed in range(0, 2 + 1): order = (qcd, qed) - if order == (0, 0): - continue gamma_ns = ( np.random.rand(qcd + 1, qed + 1) + np.random.rand(qcd + 1, qed + 1) * 1j ) @@ -54,6 +53,40 @@ def test_zero(): ) +def test_zero_true_gamma(): + """No evolution results in exp(0)""" + nf = 3 + ev_op_iterations = 2 + for mode in br.non_singlet_pids_map.values(): + if mode in [10201, 10101, 10200]: + continue + for qcd in range(1, 3 + 1): + for qed in range(0, 2 + 1): + order = (qcd, qed) + n = np.random.rand() + gamma_ns = ad.gamma_ns_qed(order, mode, n, nf) + for method in methods: + np.testing.assert_allclose( + ns.dispatcher( + order, method, gamma_ns, 1.0, 1.0, 1.0, nf, ev_op_iterations + ), + 1.0, + ) + np.testing.assert_allclose( + ns.dispatcher( + order, + method, + np.zeros((qcd + 1, qed + 1), dtype=complex), + 2.0, + 1.0, + 1.0, + nf, + ev_op_iterations, + ), + 1.0, + ) + + # def test_ode(): # aem = 0.01 # nf = 3 From 1be051c24ecc7eb12cb730b6924812f80e8a091a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 13 Sep 2022 12:30:29 +0200 Subject: [PATCH 132/312] Fix bug in the contruction of beta function --- src/eko/kernels/QEDsinglet.py | 20 +--- src/eko/kernels/QEDvalence.py | 23 ++--- tests/eko/test_kernels_QEDns.py | 165 ++++++++++++++++++++++---------- 3 files changed, 123 insertions(+), 85 deletions(-) diff --git a/src/eko/kernels/QEDsinglet.py b/src/eko/kernels/QEDsinglet.py index be73fd5a7..e3f73978d 100644 --- a/src/eko/kernels/QEDsinglet.py +++ b/src/eko/kernels/QEDsinglet.py @@ -37,22 +37,10 @@ def eko_iterate(gamma_singlet, a1, a0, aem, nf, order, ev_op_iterations): a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) e = np.identity(4, np.complex_) al = a_steps[0] - # betaQCD = np.array( - # [ - # [ - # beta.beta_qcd((2, 0), nf), - # beta.beta_qcd((3, 0), nf), - # beta.beta_qcd((4, 0), nf), - # beta.beta_qcd((5, 0), nf), - # ], - # [beta.beta_qcd((2, 1), nf), 0, 0, 0], - # [0, 0, 0, 0], - # ] - # ) betaQCD = np.zeros((4, 3), np.complex_) - for i in range(4): - betaQCD[i, 0] = beta.beta_qcd((i + 2, 0), nf) - betaQCD[0, 1] = beta.beta_qcd((2, 1), nf) + for i in range(1, 3 + 1): + betaQCD[i, 0] = beta.beta_qcd((i + 1, 0), nf) + betaQCD[1, 1] = beta.beta_qcd((2, 1), nf) for ah in a_steps[1:]: a_half = (ah + al) / 2.0 delta_a = ah - al @@ -60,7 +48,7 @@ def eko_iterate(gamma_singlet, a1, a0, aem, nf, order, ev_op_iterations): betatot = 0 for i in range(0, order[0] + 1): for j in range(0, order[1] + 1): - betatot += a_half**2 * betaQCD[i, j] * a_half**i * aem**j + betatot += a_half**1 * betaQCD[i, j] * a_half**i * aem**j gamma += gamma_singlet[i, j] * a_half**i * aem**j ln = gamma / betatot * delta_a ek = np.ascontiguousarray(ad.exp_matrix(ln)[0]) diff --git a/src/eko/kernels/QEDvalence.py b/src/eko/kernels/QEDvalence.py index 1e1813105..1a933f31f 100644 --- a/src/eko/kernels/QEDvalence.py +++ b/src/eko/kernels/QEDvalence.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +"""Collection of QED valence EKOs.""" import numba as nb import numpy as np @@ -10,7 +11,7 @@ @nb.njit(cache=True) def eko_iterate(gamma_valence, a1, a0, aem, nf, order, ev_op_iterations): """ - Singlet NLO or NNLO iterated (exact) EKO + Singlet NLO or NNLO iterated (exact) EKO. Parameters ---------- @@ -35,22 +36,10 @@ def eko_iterate(gamma_valence, a1, a0, aem, nf, order, ev_op_iterations): a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) e = np.identity(2, np.complex_) al = a_steps[0] - # betaQCD = np.array( - # [ - # [ - # beta.beta_qcd((2, 0), nf), - # beta.beta_qcd((3, 0), nf), - # beta.beta_qcd((4, 0), nf), - # beta.beta_qcd((5, 0), nf), - # ], - # [beta.beta_qcd((2, 1), nf), 0, 0, 0], - # [0, 0, 0, 0], - # ] - # ) betaQCD = np.zeros((4, 3), np.complex_) - for i in range(4): - betaQCD[i, 0] = beta.beta_qcd((i + 2, 0), nf) - betaQCD[0, 1] = beta.beta_qcd((2, 1), nf) + for i in range(1, 3 + 1): + betaQCD[i, 0] = beta.beta_qcd((i + 1, 0), nf) + betaQCD[1, 1] = beta.beta_qcd((2, 1), nf) for ah in a_steps[1:]: a_half = (ah + al) / 2.0 delta_a = ah - al @@ -58,7 +47,7 @@ def eko_iterate(gamma_valence, a1, a0, aem, nf, order, ev_op_iterations): betatot = 0 for i in range(0, order[0] + 1): for j in range(0, order[1] + 1): - betatot += a_half**2 * betaQCD[i, j] * a_half**i * aem**j + betatot += a_half**1 * betaQCD[i, j] * a_half**i * aem**j gamma += gamma_valence[i, j] * a_half**i * aem**j ln = gamma / betatot * delta_a ek = np.ascontiguousarray(ad.exp_matrix(ln)[0]) diff --git a/tests/eko/test_kernels_QEDns.py b/tests/eko/test_kernels_QEDns.py index 055e01c44..3eebb4e4d 100644 --- a/tests/eko/test_kernels_QEDns.py +++ b/tests/eko/test_kernels_QEDns.py @@ -87,59 +87,120 @@ def test_zero_true_gamma(): ) -# def test_ode(): -# aem = 0.01 -# nf = 3 -# ev_op_iterations = 10 -# delta_a = -1e-6 -# a0 = 0.3 -# betaQCD = np.zeros((4, 3), np.complex_) -# for i in range(3 + 1): -# betaQCD[i, 0] = beta.beta_qcd((i + 2, 0), nf) -# betaQCD[0, 1] = beta.beta_qcd((2, 1), nf) -# for qcd in range(1,1+1): -# for qed in range(0,0+1): -# order = (qcd, qed) -# for a1 in [0.1, 0.2]: -# gamma_ns = ( -# np.random.rand(qcd + 1, qed + 1) + np.random.rand(qcd + 1, qed + 1) * 1j -# ) -# gamma_ns[0,0] = 0. -# gammatot = 0. -# betatot = 0. -# for i in range(0, order[0] + 1): -# for j in range(0, 0 + 1): -# gammatot += gamma_ns[i, j] * a1**i * aem**j -# betatot += a1**2 * betaQCD[i, j] * a1**i * aem**j +def test_ode(): + aem = 0.01 + nf = 3 + ev_op_iterations = 10 + delta_a = -1e-6 + a0 = 0.3 + betaQCD = np.zeros((4, 3), np.complex_) + for i in range(1, 3 + 1): + betaQCD[i, 0] = beta.beta_qcd((i + 1, 0), nf) + betaQCD[1, 1] = beta.beta_qcd((2, 1), nf) + for qcd in range(1, 3 + 1): + for qed in range(1, 2 + 1): + order = (qcd, qed) + for a1 in [0.1, 0.2]: + gamma_ns = ( + np.random.rand(3 + 1, 2 + 1) + np.random.rand(3 + 1, 2 + 1) * 1j + ) + gamma_ns[0, 0] = 0.0 + gamma_ns[2, 1] = 0.0 + gamma_ns[3, 1] = 0.0 + gamma_ns[1, 2] = 0.0 + gamma_ns[2, 2] = 0.0 + gamma_ns[3, 2] = 0.0 + gammatot = 0.0 + betatot = 0.0 + for i in range(0, order[0] + 1): + for j in range(0, order[1] + 1): + gammatot += gamma_ns[i, j] * a1**i * aem**j + betatot += a1**1 * betaQCD[i, j] * a1**i * aem**j + + r = gammatot / betatot + for method in methods: + rhs = r * ns.dispatcher( + order, method, gamma_ns, a1, a0, aem, nf, ev_op_iterations + ) + lhs = ( + ns.dispatcher( + order, + method, + gamma_ns, + a1 + 0.5 * delta_a, + a0, + aem, + nf, + ev_op_iterations, + ) + - ns.dispatcher( + order, + method, + gamma_ns, + a1 - 0.5 * delta_a, + a0, + aem, + nf, + ev_op_iterations, + ) + ) / delta_a + np.testing.assert_allclose(lhs, rhs, atol=np.abs(delta_a)) + + +def test_ode_true_gamma(): + aem = 0.01 + nf = 3 + ev_op_iterations = 10 + delta_a = -1e-6 + a0 = 0.3 + betaQCD = np.zeros((4, 3), np.complex_) + for i in range(1, 3 + 1): + betaQCD[i, 0] = beta.beta_qcd((i + 1, 0), nf) + betaQCD[1, 1] = beta.beta_qcd((2, 1), nf) + for mode in br.non_singlet_pids_map.values(): + if mode in [10201, 10101, 10200]: + continue + for qcd in range(1, 3 + 1): + for qed in range(1, 2 + 1): + order = (qcd, qed) + for a1 in [0.1, 0.2]: + n = 3 + np.random.rand() + gamma_ns = ad.gamma_ns_qed(order, mode, n, nf) + gammatot = 0.0 + betatot = 0.0 + for i in range(0, order[0] + 1): + for j in range(0, order[1] + 1): + gammatot += gamma_ns[i, j] * a1**i * aem**j + betatot += a1**1 * betaQCD[i, j] * a1**i * aem**j -# r = gammatot / betatot -# for method in methods: -# rhs = r * ns.dispatcher( -# order, method, gamma_ns, a1, a0, aem, nf, ev_op_iterations -# ) -# lhs = ( -# ns.dispatcher( -# order, -# method, -# gamma_ns, -# a1 + 0.5 * delta_a, -# a0, -# aem, -# nf, -# ev_op_iterations, -# ) -# - ns.dispatcher( -# order, -# method, -# gamma_ns, -# a1 - 0.5 * delta_a, -# a0, -# aem, -# nf, -# ev_op_iterations, -# ) -# ) / delta_a -# np.testing.assert_allclose(lhs, rhs, atol=np.abs(delta_a)) + r = gammatot / betatot + for method in methods: + rhs = r * ns.dispatcher( + order, method, gamma_ns, a1, a0, aem, nf, ev_op_iterations + ) + lhs = ( + ns.dispatcher( + order, + method, + gamma_ns, + a1 + 0.5 * delta_a, + a0, + aem, + nf, + ev_op_iterations, + ) + - ns.dispatcher( + order, + method, + gamma_ns, + a1 - 0.5 * delta_a, + a0, + aem, + nf, + ev_op_iterations, + ) + ) / delta_a + np.testing.assert_allclose(lhs, rhs, atol=np.abs(delta_a)) def test_error(): From 56303115cfc2de83c5cecf1038d83c88350a9604 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 13 Sep 2022 12:38:06 +0200 Subject: [PATCH 133/312] Fix test_kernels_QEDsinglet.py test_kernels_QEDvalence.py --- tests/eko/test_kernels_QEDsinglet.py | 12 ++++-------- tests/eko/test_kernels_QEDvalence.py | 12 ++++-------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/tests/eko/test_kernels_QEDsinglet.py b/tests/eko/test_kernels_QEDsinglet.py index f8b5a27f6..43d3aa213 100644 --- a/tests/eko/test_kernels_QEDsinglet.py +++ b/tests/eko/test_kernels_QEDsinglet.py @@ -24,11 +24,9 @@ def test_zero(monkeypatch): nf = 3 ev_op_iterations = 2 ev_op_max_order = (3, 0) - for qcd in range(0, 3 + 1): - for qed in range(0, 2 + 1): + for qcd in range(1, 3 + 1): + for qed in range(1, 2 + 1): order = (qcd, qed) - if order == (0, 0): - continue gamma_s = ( np.random.rand(qcd + 1, qed + 1, 4, 4) + np.random.rand(qcd + 1, qed + 1, 4, 4) * 1j @@ -82,11 +80,9 @@ def test_zero_true_gamma(monkeypatch): nf = 3 ev_op_iterations = 2 ev_op_max_order = (3, 0) - for qcd in range(0, 3 + 1): - for qed in range(0, 2 + 1): + for qcd in range(1, 3 + 1): + for qed in range(1, 2 + 1): order = (qcd, qed) - if order == (0, 0): - continue n = np.random.rand() gamma_s = ad.gamma_singlet_qed(order, n, nf) # monkeypatch.setattr( diff --git a/tests/eko/test_kernels_QEDvalence.py b/tests/eko/test_kernels_QEDvalence.py index 897110f0d..5f0e95368 100644 --- a/tests/eko/test_kernels_QEDvalence.py +++ b/tests/eko/test_kernels_QEDvalence.py @@ -24,11 +24,9 @@ def test_zero(monkeypatch): nf = 3 ev_op_iterations = 2 ev_op_max_order = (3, 0) - for qcd in range(0, 3 + 1): - for qed in range(0, 2 + 1): + for qcd in range(1, 3 + 1): + for qed in range(1, 2 + 1): order = (qcd, qed) - if order == (0, 0): - continue gamma_v = ( np.random.rand(qcd + 1, qed + 1, 2, 2) + np.random.rand(qcd + 1, qed + 1, 2, 2) * 1j @@ -80,11 +78,9 @@ def test_zero_true_gamma(monkeypatch): nf = 3 ev_op_iterations = 2 ev_op_max_order = (3, 0) - for qcd in range(0, 3 + 1): - for qed in range(0, 2 + 1): + for qcd in range(1, 3 + 1): + for qed in range(1, 2 + 1): order = (qcd, qed) - if order == (0, 0): - continue n = np.random.rand() gamma_v = ad.gamma_valence_qed(order, n, nf) # monkeypatch.setattr( From 9128a3ccad39b80ee0b60dc779040091a5a9729e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 13 Sep 2022 17:36:52 +0200 Subject: [PATCH 134/312] Adding qed tests in test_ev_operator.py --- benchmarks/CT18_bench.py | 16 +-- src/eko/anomalous_dimensions/__init__.py | 18 +-- src/eko/evolution_operator/__init__.py | 29 ++--- tests/eko/test_ev_operator.py | 142 +++++++++++++++++++++++ 4 files changed, 169 insertions(+), 36 deletions(-) diff --git a/benchmarks/CT18_bench.py b/benchmarks/CT18_bench.py index 6aef16e85..5a8a9e682 100644 --- a/benchmarks/CT18_bench.py +++ b/benchmarks/CT18_bench.py @@ -30,14 +30,14 @@ class BenchmarkCT18(Runner): # Rotate to evolution basis rotate_to_evolution_basis = True - def benchmark_nnlo(self, Q0=1.295, Q2grid=(1e4,)): + def benchmark_nnlo(self, Q0=5.0, Q2grid=(1e4,)): theory_card = base_theory.copy() theory_card.update( { "alphas": 0.118000, "alphaqed": 0.007496, "PTO": 2, - "QED": 0, + "QED": 1, "Q0": Q0, "MaxNfPdf": 5, "MaxNfAs": 5, @@ -46,13 +46,13 @@ def benchmark_nnlo(self, Q0=1.295, Q2grid=(1e4,)): operator_card = {"Q2grid": list(Q2grid)} self.skip_pdfs = lambda _theory: [ -6, - 6, - 22, - "ph", - "T35", - "V35", + # 6, + # 22, + # "ph", + "Tu8", + "Vu8", ] - self.run([theory_card], [operator_card], ["CT18NNLO"]) + self.run([theory_card], [operator_card], ["NNPDF31_nnlo_as_0118_luxqed"]) def benchmark_znnlo(self, Q0=1.3, Q2grid=(1e4,)): theory_card = base_theory.copy() diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index b83ce1c46..c56437afa 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -296,12 +296,8 @@ def gamma_ns_qed(order, mode, n, nf): if max_weight >= 3: # here we need only S1,S2,S3,S4 sx = harmonics.sx(n, max_weight=max_weight + 1) - elif order[0] >= 1 and order[1] >= 1: - sx = harmonics.sx(n, max_weight=3) - elif order[1] == 2: - sx = harmonics.sx(n, max_weight=3) else: - sx = harmonics.sx(n, max_weight=max_weight) + sx = harmonics.sx(n, max_weight=3) # now combine gamma_ns = np.zeros((order[0] + 1, order[1] + 1), np.complex_) if order[0] >= 1: @@ -379,12 +375,8 @@ def gamma_singlet_qed(order, n, nf): if max_weight >= 3: # here we need only S1,S2,S3,S4 sx = harmonics.sx(n, max_weight=max_weight + 1) - elif order[0] >= 1 and order[1] >= 1: - sx = harmonics.sx(n, max_weight=3) - elif order[1] == 2: - sx = harmonics.sx(n, max_weight=3) else: - sx = harmonics.sx(n, max_weight=max_weight) + sx = harmonics.sx(n, max_weight=3) gamma_s = np.zeros((order[0] + 1, order[1] + 1, 4, 4), np.complex_) if order[0] >= 1: gamma_s[1, 0] = as1.gamma_QEDsinglet(n, sx[0], nf) @@ -432,12 +424,8 @@ def gamma_valence_qed(order, n, nf): if max_weight >= 3: # here we need only S1,S2,S3,S4 sx = harmonics.sx(n, max_weight=max_weight + 1) - elif order[0] >= 1 and order[1] >= 1: - sx = harmonics.sx(n, max_weight=3) - elif order[1] == 2: - sx = harmonics.sx(n, max_weight=3) else: - sx = harmonics.sx(n, max_weight=max_weight) + sx = harmonics.sx(n, max_weight=3) gamma_v = np.zeros((order[0] + 1, order[1] + 1, 2, 2), np.complex_) if order[0] >= 1: gamma_v[1, 0] = as1.gamma_QEDvalence(n, sx[0]) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 8d335ff22..4b86e969e 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -718,17 +718,20 @@ def copy_ns_ops(self): ].error = self.op_members[ (br.non_singlet_pids_map["ns-"], 0) ].error.copy() - elif self.order[1] == 1 and self.order[0] == 0: # at O(as0aem1) u-=u+, d-=d+ - self.op_members[ - (br.non_singlet_pids_map["ns-u"], 0) - ].value = self.op_members[(br.non_singlet_pids_map["ns+u"], 0)].value.copy() - self.op_members[ - (br.non_singlet_pids_map["ns-u"], 0) - ].error = self.op_members[(br.non_singlet_pids_map["ns+u"], 0)].error.copy() - self.op_members[ - (br.non_singlet_pids_map["ns-d"], 0) - ].value = self.op_members[(br.non_singlet_pids_map["ns+d"], 0)].value.copy() - self.op_members[ - (br.non_singlet_pids_map["ns-d"], 0) - ].error = self.op_members[(br.non_singlet_pids_map["ns+d"], 0)].error.copy() + # at O(as0aem1) u-=u+, d-=d+ # starting from O(as1aem1) P+ != P- + # However the solution with pure QED are not implemented in EKO + # so the ns anomalous dimensions are always different + # elif self.order[1] == 1 and self.order[0] == 0: + # self.op_members[ + # (br.non_singlet_pids_map["ns-u"], 0) + # ].value = self.op_members[(br.non_singlet_pids_map["ns+u"], 0)].value.copy() + # self.op_members[ + # (br.non_singlet_pids_map["ns-u"], 0) + # ].error = self.op_members[(br.non_singlet_pids_map["ns+u"], 0)].error.copy() + # self.op_members[ + # (br.non_singlet_pids_map["ns-d"], 0) + # ].value = self.op_members[(br.non_singlet_pids_map["ns+d"], 0)].value.copy() + # self.op_members[ + # (br.non_singlet_pids_map["ns-d"], 0) + # ].error = self.op_members[(br.non_singlet_pids_map["ns+d"], 0)].error.copy() diff --git a/tests/eko/test_ev_operator.py b/tests/eko/test_ev_operator.py index 6641e2bca..972f4a150 100644 --- a/tests/eko/test_ev_operator.py +++ b/tests/eko/test_ev_operator.py @@ -69,6 +69,26 @@ def test_quad_ker(monkeypatch): is_threshold=False, ) np.testing.assert_allclose(res_s, 1.0) + res_s = quad_ker( + u=0, + order=(1, 1), + mode0=100, + mode1=100, + method="iterate-exact", + is_log=is_log, + logx=0.123, + areas=np.zeros(3), + as1=1, + as0=2, + aem=0.00058, + nf=3, + L=0, + ev_op_iterations=0, + ev_op_max_order=(0, 0), + sv_mode=1, + is_threshold=False, + ) + np.testing.assert_allclose(res_s, 1.0) res_s = quad_ker( u=0, order=(1, 0), @@ -89,6 +109,66 @@ def test_quad_ker(monkeypatch): is_threshold=False, ) np.testing.assert_allclose(res_s, 0.0) + res_s = quad_ker( + u=0, + order=(1, 1), + mode0=100, + mode1=21, + method="iterate-exact", + is_log=is_log, + logx=0.0, + areas=np.zeros(3), + as1=1, + as0=2, + aem=0.00058, + nf=3, + L=0, + ev_op_iterations=0, + ev_op_max_order=(0, 0), + sv_mode=1, + is_threshold=False, + ) + np.testing.assert_allclose(res_s, 0.0) + res_v = quad_ker( + u=0, + order=(1, 1), + mode0=10200, + mode1=10200, + method="iterate-exact", + is_log=is_log, + logx=0.123, + areas=np.zeros(3), + as1=1, + as0=2, + aem=0.00058, + nf=3, + L=0, + ev_op_iterations=0, + ev_op_max_order=(0, 0), + sv_mode=1, + is_threshold=False, + ) + np.testing.assert_allclose(res_v, 1.0) + res_v = quad_ker( + u=0, + order=(1, 1), + mode0=10200, + mode1=10201, + method="iterate-exact", + is_log=is_log, + logx=0.123, + areas=np.zeros(3), + as1=1, + as0=2, + aem=0.00058, + nf=3, + L=0, + ev_op_iterations=0, + ev_op_max_order=(0, 0), + sv_mode=1, + is_threshold=False, + ) + np.testing.assert_allclose(res_v, 0.0) for label in [(br.non_singlet_pids_map["ns+"], 0), (100, 100)]: for sv in [2, 3]: res_sv = quad_ker( @@ -111,6 +191,28 @@ def test_quad_ker(monkeypatch): is_threshold=False, ) np.testing.assert_allclose(res_sv, 1.0) + for label in [(100, 100), (10200, 10200)]: + for sv in [2, 3]: + res_sv = quad_ker( + u=0, + order=(1, 1), + mode0=label[0], + mode1=label[1], + method="iterate-exact", + is_log=True, + logx=0.123, + areas=np.zeros(3), + as1=1, + as0=2, + aem=0.00058, + nf=3, + L=0, + ev_op_iterations=0, + ev_op_max_order=(1, 0), + sv_mode=sv, + is_threshold=False, + ) + np.testing.assert_allclose(res_sv, 1.0) monkeypatch.setattr(interpolation, "log_evaluate_Nx", lambda *args: 0) res_ns = quad_ker( @@ -203,6 +305,34 @@ def test_labels(self): ) assert sorted(o.labels) == [] + def test_labels_qed(self): + o = Operator( + dict( + order=(3, 1), + debug_skip_non_singlet=False, + debug_skip_singlet=False, + n_integration_cores=1, + ), + {}, + 3, + 1, + 2, + ) + assert sorted(o.labels) == sorted(br.full_unified_labels) + o = Operator( + dict( + order=(2, 1), + debug_skip_non_singlet=True, + debug_skip_singlet=True, + n_integration_cores=1, + ), + {}, + 3, + 1, + 2, + ) + assert sorted(o.labels) == [] + def test_n_pools(self): excluded_cores = 3 # make sure we actually have more the those cores (e.g. on github we don't) @@ -339,6 +469,18 @@ def test_compute(self, monkeypatch): assert k in o1.op_members np.testing.assert_allclose(o1.op_members[k].value, np.eye(2), err_msg=k) + for n in range(1, 3 + 1): + for qed in range(1, 2 + 1): + g.config["order"] = (n, qed) + o1 = Operator(g.config, g.managers, 3, 2.0, 2.0) + # o1.config["order"] = (n, qed) + o1.compute() + for k in br.non_singlet_unified_labels: + assert k in o1.op_members + np.testing.assert_allclose( + o1.op_members[k].value, np.eye(2), err_msg=k + ) + def test_pegasus_path(): def quad_ker_pegasus( From 042966a3d25dd2fc63cb8bcc8f08fa767041b12f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 14 Sep 2022 09:25:25 +0200 Subject: [PATCH 135/312] Revert CT18_bench.py --- benchmarks/CT18_bench.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/benchmarks/CT18_bench.py b/benchmarks/CT18_bench.py index 5a8a9e682..6aef16e85 100644 --- a/benchmarks/CT18_bench.py +++ b/benchmarks/CT18_bench.py @@ -30,14 +30,14 @@ class BenchmarkCT18(Runner): # Rotate to evolution basis rotate_to_evolution_basis = True - def benchmark_nnlo(self, Q0=5.0, Q2grid=(1e4,)): + def benchmark_nnlo(self, Q0=1.295, Q2grid=(1e4,)): theory_card = base_theory.copy() theory_card.update( { "alphas": 0.118000, "alphaqed": 0.007496, "PTO": 2, - "QED": 1, + "QED": 0, "Q0": Q0, "MaxNfPdf": 5, "MaxNfAs": 5, @@ -46,13 +46,13 @@ def benchmark_nnlo(self, Q0=5.0, Q2grid=(1e4,)): operator_card = {"Q2grid": list(Q2grid)} self.skip_pdfs = lambda _theory: [ -6, - # 6, - # 22, - # "ph", - "Tu8", - "Vu8", + 6, + 22, + "ph", + "T35", + "V35", ] - self.run([theory_card], [operator_card], ["NNPDF31_nnlo_as_0118_luxqed"]) + self.run([theory_card], [operator_card], ["CT18NNLO"]) def benchmark_znnlo(self, Q0=1.3, Q2grid=(1e4,)): theory_card = base_theory.copy() From db471deb705dca75f825f554c872d99c331f8307 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 14 Sep 2022 12:44:06 +0200 Subject: [PATCH 136/312] Test ns scale variation --- src/eko/evolution_operator/__init__.py | 13 +++++----- tests/eko/test_ev_operator.py | 34 ++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 4b86e969e..8a55e7259 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -296,8 +296,9 @@ def quad_ker( # scale var exponentiated is directly applied on gamma if sv_mode == sv.Modes.exponentiated: # the aem terms do not get scale variations - gamma_s[0][1:] = sv.exponentiated.gamma_variation( - gamma_s[0][1:], order, nf, L + # TODO : double check it + gamma_s[1:, 0] = sv.exponentiated.gamma_variation( + gamma_s[1:, 0], order, nf, L ) ker = qed_s.dispatcher( order, @@ -321,8 +322,8 @@ def quad_ker( gamma_v = ad.gamma_valence_qed(order, ker_base.n, nf) # scale var exponentiated is directly applied on gamma if sv_mode == sv.Modes.exponentiated: - gamma_v[0][1:] = sv.exponentiated.gamma_variation( - gamma_v[0][1:], order, nf, L + gamma_v[1:, 0] = sv.exponentiated.gamma_variation( + gamma_v[1:, 0], order, nf, L ) ker = qed_v.dispatcher( order, @@ -345,8 +346,8 @@ def quad_ker( gamma_ns = ad.gamma_ns_qed(order, mode0, ker_base.n, nf) # scale var exponentiated is directly applied on gamma if sv_mode == sv.Modes.exponentiated: - gamma_ns[0][1:] = sv.exponentiated.gamma_variation( - gamma_ns[0][1:], order, nf, L + gamma_ns[1:, 0] = sv.exponentiated.gamma_variation( + gamma_ns[1:, 0], order, nf, L ) ker = qed_ns.dispatcher( order, diff --git a/tests/eko/test_ev_operator.py b/tests/eko/test_ev_operator.py index 972f4a150..e32f70de6 100644 --- a/tests/eko/test_ev_operator.py +++ b/tests/eko/test_ev_operator.py @@ -12,6 +12,7 @@ from eko.evolution_operator import Operator, quad_ker from eko.evolution_operator.grid import OperatorGrid from eko.interpolation import InterpolatorDispatcher +from eko.kernels import QEDnon_singlet as qed_ns from eko.kernels import non_singlet as ns from eko.kernels import singlet as s from eko.thresholds import ThresholdsAtlas @@ -27,6 +28,7 @@ def test_quad_ker(monkeypatch): monkeypatch.setattr(interpolation, "log_evaluate_Nx", lambda *args: 1) monkeypatch.setattr(interpolation, "evaluate_Nx", lambda *args: 1) monkeypatch.setattr(ns, "dispatcher", lambda *args: 1.0) + monkeypatch.setattr(qed_ns, "dispatcher", lambda *args: 1.0) monkeypatch.setattr(s, "dispatcher", lambda *args: np.identity(2)) for is_log in [True, False]: res_ns = quad_ker( @@ -49,6 +51,26 @@ def test_quad_ker(monkeypatch): is_threshold=False, ) np.testing.assert_allclose(res_ns, 0.0) + res_ns = quad_ker( + u=0, + order=(3, 1), + mode0=br.non_singlet_pids_map["ns+u"], + mode1=0, + method="", + is_log=is_log, + logx=0.0, + areas=np.zeros(3), + as1=1, + as0=2, + aem=0.00058, + nf=3, + L=0, + ev_op_iterations=0, + ev_op_max_order=(0, 0), + sv_mode=1, + is_threshold=False, + ) + np.testing.assert_allclose(res_ns, 0.0) res_s = quad_ker( u=0, order=(1, 0), @@ -153,7 +175,7 @@ def test_quad_ker(monkeypatch): u=0, order=(1, 1), mode0=10200, - mode1=10201, + mode1=10204, method="iterate-exact", is_log=is_log, logx=0.123, @@ -191,7 +213,15 @@ def test_quad_ker(monkeypatch): is_threshold=False, ) np.testing.assert_allclose(res_sv, 1.0) - for label in [(100, 100), (10200, 10200)]: + for label in [ + (100, 100), + (21, 21), + (22, 22), + (101, 101), + (10200, 10200), + (10204, 10204), + (10202, 0), + ]: for sv in [2, 3]: res_sv = quad_ker( u=0, From f90811354a4ac5032e38b7cb078c1154b5dd2cd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 14 Sep 2022 12:51:59 +0200 Subject: [PATCH 137/312] Change names in select_QEDsinglet_element --- src/eko/evolution_operator/__init__.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 8a55e7259..31676dcb9 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -70,22 +70,22 @@ def select_QEDsinglet_element(ker, mode0, mode1): singlet integration kernel element """ if mode0 == 21: - k = 0 + index1 = 0 elif mode0 == 22: - k = 1 + index1 = 1 elif mode0 == 100: - k = 2 + index1 = 2 else: - k = 3 + index1 = 3 if mode1 == 21: - l = 0 + index2 = 0 elif mode1 == 22: - l = 1 + index2 = 1 elif mode1 == 100: - l = 2 + index2 = 2 else: - l = 3 - return ker[k, l] + index2 = 3 + return ker[index1, index2] @nb.njit(cache=True) @@ -106,9 +106,9 @@ def select_QEDvalence_element(ker, mode0, mode1): ker : complex singlet integration kernel element """ - k = 0 if mode0 == 10200 else 1 - l = 0 if mode1 == 10200 else 1 - return ker[k, l] + index1 = 0 if mode0 == 10200 else 1 + index2 = 0 if mode1 == 10200 else 1 + return ker[index1, index2] spec = [ From ea5b33e0807fbd2b3c2cfd98ecaf3c068385a813 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 14 Sep 2022 14:28:28 +0200 Subject: [PATCH 138/312] Refactor e2u and e2d in gamma_ns_qed --- src/eko/anomalous_dimensions/__init__.py | 41 ++++++++++++++++-------- tests/eko/test_ad.py | 2 +- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index c56437afa..38f6cdbe0 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -303,21 +303,12 @@ def gamma_ns_qed(order, mode, n, nf): if order[0] >= 1: gamma_ns[1, 0] = as1.gamma_ns(n, sx[0]) if order[1] >= 1: - if mode in [10102, 10202]: - gamma_ns[0, 1] = constants.eu2 * aem1.gamma_ns(n, sx) - if mode in [10103, 10203]: - gamma_ns[0, 1] = constants.ed2 * aem1.gamma_ns(n, sx) + gamma_ns[0, 1] = e2(mode) * aem1.gamma_ns(n, sx) if order[0] >= 1 and order[1] >= 1: - if mode == 10102: - gamma_ns[1, 1] = constants.eu2 * as1aem1.gamma_nsp(n, sx) - elif mode == 10103: - gamma_ns[1, 1] = constants.ed2 * as1aem1.gamma_nsp(n, sx) - elif mode == 10202: - gamma_ns[1, 1] = constants.eu2 * as1aem1.gamma_nsm(n, sx) - elif mode == 10203: - gamma_ns[1, 1] = constants.ed2 * as1aem1.gamma_nsm(n, sx) - else: - raise NotImplementedError("Non-singlet sector is not implemented") + if mode in [10102, 10103]: + gamma_ns[1, 1] = e2(mode) * as1aem1.gamma_nsp(n, sx) + elif mode in [10202, 10203]: + gamma_ns[1, 1] = e2(mode) * as1aem1.gamma_nsm(n, sx) # NLO and beyond if order[0] >= 2: if mode in [10102, 10103]: @@ -345,6 +336,28 @@ def gamma_ns_qed(order, mode, n, nf): return gamma_ns +def e2(mode): + r""" + Compute the charge of a given non-singlet mode. + + Parameters + ---------- + mode : int + evolution mode + + Returns + ------- + charge : float + charge of selected mode + """ + if mode in [br.non_singlet_pids_map["ns-u"], br.non_singlet_pids_map["ns+u"]]: + return constants.eu2 + elif mode in [br.non_singlet_pids_map["ns-d"], br.non_singlet_pids_map["ns+d"]]: + return constants.ed2 + else: + raise NotImplementedError("Non-singlet sector is not implemented") + + @nb.njit(cache=True) def gamma_singlet_qed(order, n, nf): r""" diff --git a/tests/eko/test_ad.py b/tests/eko/test_ad.py index 20941dc49..229c18cbd 100644 --- a/tests/eko/test_ad.py +++ b/tests/eko/test_ad.py @@ -225,6 +225,6 @@ def test_dim_nsp(): gamma_nsdp = ad.gamma_ns_qed((3, 2), 10103, N, nf) assert gamma_nsdp.shape == (4, 3) with pytest.raises(NotImplementedError): - gamma_ns = ad.gamma_ns_qed((1, 1), 10106, N, nf) + gamma_ns = ad.gamma_ns_qed((2, 2), 10106, N, nf) with pytest.raises(NotImplementedError): gamma_ns = ad.gamma_ns_qed((2, 0), 10106, N, nf) From b21b5e2dbc6498bef621cfa15c9214b838f6f25a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 14 Sep 2022 15:29:15 +0200 Subject: [PATCH 139/312] Add missing nb.njit to e2(mode) --- src/eko/anomalous_dimensions/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index 38f6cdbe0..d9939b875 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -336,6 +336,7 @@ def gamma_ns_qed(order, mode, n, nf): return gamma_ns +@nb.njit(cache=True) def e2(mode): r""" Compute the charge of a given non-singlet mode. From 1c9d018d65e3d83d041e030a35a224e4f9cb65de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 14 Sep 2022 16:01:34 +0200 Subject: [PATCH 140/312] Revert "Add missing nb.njit to e2(mode)" This reverts commit b21b5e2dbc6498bef621cfa15c9214b838f6f25a. --- src/eko/anomalous_dimensions/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index d9939b875..38f6cdbe0 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -336,7 +336,6 @@ def gamma_ns_qed(order, mode, n, nf): return gamma_ns -@nb.njit(cache=True) def e2(mode): r""" Compute the charge of a given non-singlet mode. From ca727cd47800a1ce0c1256c669d7343eebca830f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 14 Sep 2022 16:03:15 +0200 Subject: [PATCH 141/312] Revert "Refactor e2u and e2d in gamma_ns_qed" This reverts commit ea5b33e0807fbd2b3c2cfd98ecaf3c068385a813. --- src/eko/anomalous_dimensions/__init__.py | 41 ++++++++---------------- tests/eko/test_ad.py | 2 +- 2 files changed, 15 insertions(+), 28 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index 38f6cdbe0..c56437afa 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -303,12 +303,21 @@ def gamma_ns_qed(order, mode, n, nf): if order[0] >= 1: gamma_ns[1, 0] = as1.gamma_ns(n, sx[0]) if order[1] >= 1: - gamma_ns[0, 1] = e2(mode) * aem1.gamma_ns(n, sx) + if mode in [10102, 10202]: + gamma_ns[0, 1] = constants.eu2 * aem1.gamma_ns(n, sx) + if mode in [10103, 10203]: + gamma_ns[0, 1] = constants.ed2 * aem1.gamma_ns(n, sx) if order[0] >= 1 and order[1] >= 1: - if mode in [10102, 10103]: - gamma_ns[1, 1] = e2(mode) * as1aem1.gamma_nsp(n, sx) - elif mode in [10202, 10203]: - gamma_ns[1, 1] = e2(mode) * as1aem1.gamma_nsm(n, sx) + if mode == 10102: + gamma_ns[1, 1] = constants.eu2 * as1aem1.gamma_nsp(n, sx) + elif mode == 10103: + gamma_ns[1, 1] = constants.ed2 * as1aem1.gamma_nsp(n, sx) + elif mode == 10202: + gamma_ns[1, 1] = constants.eu2 * as1aem1.gamma_nsm(n, sx) + elif mode == 10203: + gamma_ns[1, 1] = constants.ed2 * as1aem1.gamma_nsm(n, sx) + else: + raise NotImplementedError("Non-singlet sector is not implemented") # NLO and beyond if order[0] >= 2: if mode in [10102, 10103]: @@ -336,28 +345,6 @@ def gamma_ns_qed(order, mode, n, nf): return gamma_ns -def e2(mode): - r""" - Compute the charge of a given non-singlet mode. - - Parameters - ---------- - mode : int - evolution mode - - Returns - ------- - charge : float - charge of selected mode - """ - if mode in [br.non_singlet_pids_map["ns-u"], br.non_singlet_pids_map["ns+u"]]: - return constants.eu2 - elif mode in [br.non_singlet_pids_map["ns-d"], br.non_singlet_pids_map["ns+d"]]: - return constants.ed2 - else: - raise NotImplementedError("Non-singlet sector is not implemented") - - @nb.njit(cache=True) def gamma_singlet_qed(order, n, nf): r""" diff --git a/tests/eko/test_ad.py b/tests/eko/test_ad.py index 229c18cbd..20941dc49 100644 --- a/tests/eko/test_ad.py +++ b/tests/eko/test_ad.py @@ -225,6 +225,6 @@ def test_dim_nsp(): gamma_nsdp = ad.gamma_ns_qed((3, 2), 10103, N, nf) assert gamma_nsdp.shape == (4, 3) with pytest.raises(NotImplementedError): - gamma_ns = ad.gamma_ns_qed((2, 2), 10106, N, nf) + gamma_ns = ad.gamma_ns_qed((1, 1), 10106, N, nf) with pytest.raises(NotImplementedError): gamma_ns = ad.gamma_ns_qed((2, 0), 10106, N, nf) From e0dee84260950c1402f52a58b75471c0a498a974 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 14 Sep 2022 17:46:09 +0200 Subject: [PATCH 142/312] Factor calling of qed non-singlet ad --- src/eko/anomalous_dimensions/__init__.py | 110 ++++++++++++++++++----- 1 file changed, 88 insertions(+), 22 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index c56437afa..d238bfecd 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -303,21 +303,9 @@ def gamma_ns_qed(order, mode, n, nf): if order[0] >= 1: gamma_ns[1, 0] = as1.gamma_ns(n, sx[0]) if order[1] >= 1: - if mode in [10102, 10202]: - gamma_ns[0, 1] = constants.eu2 * aem1.gamma_ns(n, sx) - if mode in [10103, 10203]: - gamma_ns[0, 1] = constants.ed2 * aem1.gamma_ns(n, sx) + gamma_ns[0, 1] = choose_ad_aem1(mode, n, sx) if order[0] >= 1 and order[1] >= 1: - if mode == 10102: - gamma_ns[1, 1] = constants.eu2 * as1aem1.gamma_nsp(n, sx) - elif mode == 10103: - gamma_ns[1, 1] = constants.ed2 * as1aem1.gamma_nsp(n, sx) - elif mode == 10202: - gamma_ns[1, 1] = constants.eu2 * as1aem1.gamma_nsm(n, sx) - elif mode == 10203: - gamma_ns[1, 1] = constants.ed2 * as1aem1.gamma_nsm(n, sx) - else: - raise NotImplementedError("Non-singlet sector is not implemented") + gamma_ns[1, 1] = choose_ad_as1aem1(mode, n, sx) # NLO and beyond if order[0] >= 2: if mode in [10102, 10103]: @@ -328,14 +316,7 @@ def gamma_ns_qed(order, mode, n, nf): else: raise NotImplementedError("Non-singlet sector is not implemented") if order[1] >= 2: - if mode == 10102: - gamma_ns[0, 2] = constants.eu2 * aem2.gamma_nspu(n, nf, sx) - if mode == 10103: - gamma_ns[0, 2] = constants.ed2 * aem2.gamma_nspd(n, nf, sx) - if mode == 10202: - gamma_ns[0, 2] = constants.eu2 * aem2.gamma_nsmu(n, nf, sx) - if mode == 10203: - gamma_ns[0, 2] = constants.ed2 * aem2.gamma_nsmd(n, nf, sx) + gamma_ns[0, 2] = choose_ad_aem2(mode, n, nf, sx) # NNLO and beyond if order[0] >= 3: if mode in [10102, 10103]: @@ -345,6 +326,91 @@ def gamma_ns_qed(order, mode, n, nf): return gamma_ns +@nb.njit(cache=True) +def choose_ad_aem1(mode, n, sx): + r""" + Select the non-singlet anomalous dimension at O(aem1) with the correct charge factor. + + Parameters + ---------- + mode : 10102 | 10202 | 10103 | 10203 + sector identifier + n : complex + Mellin variable + nf : int + Number of active flavors + + Returns + ------- + gamma_ns : numpy.ndarray + non-singlet anomalous dimensions + """ + if mode in [10102, 10202]: + return constants.eu2 * aem1.gamma_ns(n, sx) + if mode in [10103, 10203]: + return constants.ed2 * aem1.gamma_ns(n, sx) + else: + raise NotImplementedError("Non-singlet sector is not implemented") + + +@nb.njit(cache=True) +def choose_ad_as1aem1(mode, n, sx): + r""" + Select the non-singlet anomalous dimension at O(as1aem1) with the correct charge factor. + + Parameters + ---------- + mode : 10102 | 10202 | 10103 | 10203 + sector identifier + n : complex + Mellin variable + nf : int + Number of active flavors + + Returns + ------- + gamma_ns : numpy.ndarray + non-singlet anomalous dimensions + """ + if mode == 10102: + return constants.eu2 * as1aem1.gamma_nsp(n, sx) + elif mode == 10103: + return constants.ed2 * as1aem1.gamma_nsp(n, sx) + elif mode == 10202: + return constants.eu2 * as1aem1.gamma_nsm(n, sx) + elif mode == 10203: + return constants.ed2 * as1aem1.gamma_nsm(n, sx) + + +@nb.njit(cache=True) +def choose_ad_aem2(mode, n, nf, sx): + r""" + Select the non-singlet anomalous dimension at O(aem2) with the correct charge factor. + + Parameters + ---------- + mode : 10102 | 10202 | 10103 | 10203 + sector identifier + n : complex + Mellin variable + nf : int + Number of active flavors + + Returns + ------- + gamma_ns : numpy.ndarray + non-singlet anomalous dimensions + """ + if mode == 10102: + return constants.eu2 * aem2.gamma_nspu(n, nf, sx) + elif mode == 10103: + return constants.ed2 * aem2.gamma_nspd(n, nf, sx) + elif mode == 10202: + return constants.eu2 * aem2.gamma_nsmu(n, nf, sx) + elif mode == 10203: + return constants.ed2 * aem2.gamma_nsmd(n, nf, sx) + + @nb.njit(cache=True) def gamma_singlet_qed(order, n, nf): r""" From ceede58eed90bd1bc11b1c48493947f4fde38035 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 14 Sep 2022 18:03:21 +0200 Subject: [PATCH 143/312] Rename one-digit variable --- src/eko/basis_rotation.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/eko/basis_rotation.py b/src/eko/basis_rotation.py index 8a25a5fe6..867fd695e 100644 --- a/src/eko/basis_rotation.py +++ b/src/eko/basis_rotation.py @@ -280,20 +280,21 @@ def ad_projector(ad_lab, nf, qed=False): """ if not qed: proj = np.zeros_like(rotate_flavor_to_evolution, dtype=float) - l = map_ad_to_evolution[ad_lab] + sector = map_ad_to_evolution[ad_lab] basis = evol_basis rotate = rotate_flavor_to_evolution.copy() else: proj = np.zeros_like(rotate_flavor_to_unified_evolution, dtype=float) - l = map_ad_to_unified_evolution[ad_lab] + sector = map_ad_to_unified_evolution[ad_lab] basis = unified_evol_basis rotate = rotate_flavor_to_unified_evolution.copy() # restrict the evolution basis to light flavors # NOTE: the cut is only needed for "NS_p" and "NS_m", but the other lists # are 1-long so they are unaffected - l = l[: (nf - 1)] + # TODO : check this for unified evol + sector = sector[: (nf - 1)] - for el in l: + for el in sector: out_name, in_name = el.split(".") out_idx = basis.index(out_name) in_idx = basis.index(in_name) From 5db58e2a951a0aeeadde53712d28187a2ca5da0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 15 Sep 2022 10:23:32 +0200 Subject: [PATCH 144/312] Change name os choose_ad_aem1 ecc --- src/eko/anomalous_dimensions/__init__.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index d238bfecd..9a778827e 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -303,9 +303,9 @@ def gamma_ns_qed(order, mode, n, nf): if order[0] >= 1: gamma_ns[1, 0] = as1.gamma_ns(n, sx[0]) if order[1] >= 1: - gamma_ns[0, 1] = choose_ad_aem1(mode, n, sx) + gamma_ns[0, 1] = choose_ns_ad_aem1(mode, n, sx) if order[0] >= 1 and order[1] >= 1: - gamma_ns[1, 1] = choose_ad_as1aem1(mode, n, sx) + gamma_ns[1, 1] = choose_ns_ad_as1aem1(mode, n, sx) # NLO and beyond if order[0] >= 2: if mode in [10102, 10103]: @@ -316,7 +316,7 @@ def gamma_ns_qed(order, mode, n, nf): else: raise NotImplementedError("Non-singlet sector is not implemented") if order[1] >= 2: - gamma_ns[0, 2] = choose_ad_aem2(mode, n, nf, sx) + gamma_ns[0, 2] = choose_ns_ad_aem2(mode, n, nf, sx) # NNLO and beyond if order[0] >= 3: if mode in [10102, 10103]: @@ -327,7 +327,7 @@ def gamma_ns_qed(order, mode, n, nf): @nb.njit(cache=True) -def choose_ad_aem1(mode, n, sx): +def choose_ns_ad_aem1(mode, n, sx): r""" Select the non-singlet anomalous dimension at O(aem1) with the correct charge factor. @@ -354,7 +354,7 @@ def choose_ad_aem1(mode, n, sx): @nb.njit(cache=True) -def choose_ad_as1aem1(mode, n, sx): +def choose_ns_ad_as1aem1(mode, n, sx): r""" Select the non-singlet anomalous dimension at O(as1aem1) with the correct charge factor. @@ -383,7 +383,7 @@ def choose_ad_as1aem1(mode, n, sx): @nb.njit(cache=True) -def choose_ad_aem2(mode, n, nf, sx): +def choose_ns_ad_aem2(mode, n, nf, sx): r""" Select the non-singlet anomalous dimension at O(aem2) with the correct charge factor. From f1e0fd80a91ddce33da4b109908d7e9c7239bcf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 15 Sep 2022 11:43:30 +0200 Subject: [PATCH 145/312] Add bench for NNPDF31_luxqed in NNPDF_bench.py --- benchmarks/NNPDF_bench.py | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/benchmarks/NNPDF_bench.py b/benchmarks/NNPDF_bench.py index 358b8ee56..daacb0682 100644 --- a/benchmarks/NNPDF_bench.py +++ b/benchmarks/NNPDF_bench.py @@ -63,6 +63,33 @@ def benchmark_nlo(self, Q0=1.65, Q2grid=(100,)): self.run([theory_card], [operator_card], ["NNPDF31_nlo_as_0118"]) +class BenchmarkNNPDF31_luxqed(BenchmarkNNPDF): + """Benchmark NNPDF3.1_luxqed""" + + def benchmark_nnlo(self, Q0=1.65, Q2grid=(100,)): + theory_card = { + **base_theory, + "PTO": 2, + "QED": 1, + "Q0": Q0, + } + theory_card.update( + { + "ModEv": "iterate-exact", + } + ) + + self.skip_pdfs = lambda _theory: [ + -6, + 6, + "Tu8", + "Vu8", + ] + + operator_card = {**base_operator, "Q2grid": list(Q2grid)} + self.run([theory_card], [operator_card], ["NNPDF31_nnlo_as_0118_luxqed"]) + + class BenchmarkNNPDF40(BenchmarkNNPDF): """Benchmark NNPDF4.0""" @@ -99,6 +126,8 @@ def benchmark_nnlo(self, Q0=1.65, Q2grid=(100,)): # nn31.benchmark_nlo(Q0=np.sqrt(low2), Q2grid=[10]) # # test backward # #nn31.benchmark_nlo(Q0=np.sqrt(high2), Q2grid=[low2]) - nn40 = BenchmarkNNPDF40() + nn31qed = BenchmarkNNPDF31_luxqed() + nn31qed.benchmark_nnlo(Q0=30.0, Q2grid=(10000,)) + # nn40 = BenchmarkNNPDF40() # nn40.benchmark_nnlo(Q2grid=[100]) - nn40.benchmark_nnlo(Q0=np.sqrt(high2), Q2grid=[low2]) + # nn40.benchmark_nnlo(Q0=np.sqrt(high2), Q2grid=[low2]) From de8d7318bad1b6dda05923beeb766abf50a6d50c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 15 Sep 2022 17:55:36 +0200 Subject: [PATCH 146/312] Write QEDsinglet matrix explicitly --- src/eko/anomalous_dimensions/aem1.py | 98 +++++++++++--- src/eko/anomalous_dimensions/as1.py | 182 ++++++++++++++----------- src/eko/anomalous_dimensions/as2.py | 193 ++++++++++++++------------ src/eko/anomalous_dimensions/as3.py | 194 +++++++++++++++------------ 4 files changed, 395 insertions(+), 272 deletions(-) diff --git a/src/eko/anomalous_dimensions/aem1.py b/src/eko/anomalous_dimensions/aem1.py index 36e24b045..1696b2414 100644 --- a/src/eko/anomalous_dimensions/aem1.py +++ b/src/eko/anomalous_dimensions/aem1.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -""" -This file contains the O(aem1) Altarelli-Parisi splitting kernels. -""" +"""Contains the O(aem1) Altarelli-Parisi splitting kernels.""" import numba as nb import numpy as np @@ -12,8 +10,8 @@ @nb.njit(cache=True) def gamma_phq(N): - """ - Computes the leading-order photon-quark anomalous dimension + r""" + Compute the leading-order photon-quark anomalous dimension. Implements Eq. (2.5) of :cite:`Carrazza:2015dea`. @@ -27,14 +25,13 @@ def gamma_phq(N): gamma_phq : complex Leading-order photon-quark anomalous dimension :math:`\\gamma_{\\gamma q}^{(0)}(N)` """ - return as1.gamma_gq(N) / constants.CF @nb.njit(cache=True) def gamma_qph(N, nf): - """ - Computes the leading-order quark-photon anomalous dimension + r""" + Compute the leading-order quark-photon anomalous dimension. Implements Eq. (2.5) of :cite:`Carrazza:2015dea`. But adding the :math:`N_C` and the :math:`2n_f` factors from :math:`\\theta` inside the @@ -57,8 +54,8 @@ def gamma_qph(N, nf): @nb.njit(cache=True) def gamma_phph(nf): - """ - Computes the leading-order photon-photon anomalous dimension + r""" + Compute the leading-order photon-photon anomalous dimension. Implements Eq. (2.5) of :cite:`Carrazza:2015dea`. @@ -72,7 +69,6 @@ def gamma_phph(nf): gamma_phph : complex Leading-order phton-photon anomalous dimension :math:`\\gamma_{\\gamma \\gamma}^{(0)}(N)` """ - nu = constants.uplike_flavors(nf) nd = nf - nu return 4 / 3 * constants.NC * (nu * constants.eu2 + nd * constants.ed2) @@ -80,8 +76,8 @@ def gamma_phph(nf): @nb.njit(cache=True) def gamma_ns(N, sx): - """ - Computes the leading-order non-singlet QED anomalous dimension. + r""" + Compute the leading-order non-singlet QED anomalous dimension. Implements Eq. (2.5) of :cite:`Carrazza:2015dea`. @@ -103,8 +99,40 @@ def gamma_ns(N, sx): @nb.njit(cache=True) def gamma_singlet(N, nf, sx): + r""" + Compute the leading-order singlet anomalous dimension matrix. + + .. math:: + \gamma_S^{(0)} = \left(\begin{array}{cc} + \gamma_{qq}^{(0)} & \gamma_{qg}^{(0)}\\ + \gamma_{gq}^{(0)} & \gamma_{gg}^{(0)} + \end{array}\right) + + Parameters + ---------- + N : complex + Mellin moment + s1 : complex + harmonic sum :math:`S_{1}` + nf : int + Number of active flavors + + Returns + ------- + gamma_S_0 : numpy.ndarray + Leading-order singlet anomalous dimension matrix :math:`\gamma_{S}^{(0)}(N)` + + See Also + -------- + gamma_ns : :math:`\gamma_{qq}^{(0)}` + gamma_qg : :math:`\gamma_{qg}^{(0)}` + gamma_gq : :math:`\gamma_{gq}^{(0)}` + gamma_gg : :math:`\gamma_{gg}^{(0)}` + """ e2avg = constants.e2avg(nf) e2delta = constants.vde2m(nf) - constants.vue2m(nf) + constants.e2avg(nf) + vue2m = constants.vue2m(nf) + vde2m = constants.vde2m(nf) gamma_S_01 = np.array( [ [0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j], @@ -112,18 +140,18 @@ def gamma_singlet(N, nf, sx): 0.0 + 0.0j, gamma_phph(nf), e2avg * gamma_phq(N), - constants.vue2m(nf) * gamma_phq(N), + vue2m * gamma_phq(N), ], [ 0.0 + 0.0j, e2avg * gamma_qph(N, nf), e2avg * gamma_ns(N, sx), - constants.vue2m(nf) * gamma_ns(N, sx), + vue2m * gamma_ns(N, sx), ], [ 0.0 + 0.0j, - constants.vde2m(nf) * gamma_qph(N, nf), - constants.vde2m(nf) * gamma_ns(N, sx), + vde2m * gamma_qph(N, nf), + vde2m * gamma_ns(N, sx), e2delta * gamma_ns(N, sx), ], ], @@ -134,12 +162,42 @@ def gamma_singlet(N, nf, sx): @nb.njit(cache=True) def gamma_valence(N, nf, sx): + r""" + Compute the leading-order valence anomalous dimension matrix. + + .. math:: + \gamma_S^{(0)} = \left(\begin{array}{cc} + \gamma_{qq}^{(0)} & \gamma_{qg}^{(0)}\\ + \gamma_{gq}^{(0)} & \gamma_{gg}^{(0)} + \end{array}\right) + + Parameters + ---------- + N : complex + Mellin moment + s1 : complex + harmonic sum :math:`S_{1}` + + Returns + ------- + gamma_S_0 : numpy.ndarray + Leading-order singlet anomalous dimension matrix :math:`\gamma_{S}^{(0)}(N)` + + See Also + -------- + gamma_ns : :math:`\gamma_{qq}^{(0)}` + gamma_qg : :math:`\gamma_{qg}^{(0)}` + gamma_gq : :math:`\gamma_{gq}^{(0)}` + gamma_gg : :math:`\gamma_{gg}^{(0)}` + """ e2avg = constants.e2avg(nf) - e2delta = constants.vde2m(nf) - constants.vue2m(nf) + constants.e2avg(nf) + vue2m = constants.vue2m(nf) + vde2m = constants.vde2m(nf) + e2delta = vde2m - vue2m + e2avg gamma_V_01 = np.array( [ - [e2avg, constants.vue2m(nf)], - [constants.vde2m(nf), e2delta], + [e2avg, vue2m], + [vde2m, e2delta], ], np.complex_, ) diff --git a/src/eko/anomalous_dimensions/as1.py b/src/eko/anomalous_dimensions/as1.py index 8b9a41a29..8c1830261 100644 --- a/src/eko/anomalous_dimensions/as1.py +++ b/src/eko/anomalous_dimensions/as1.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -"""This file contains the leading-order Altarelli-Parisi splitting kernels.""" +"""Compute the leading-order Altarelli-Parisi splitting kernels.""" import numba as nb import numpy as np @@ -9,8 +9,8 @@ @nb.njit(cache=True) def gamma_ns(N, s1): - """ - Computes the leading-order non-singlet anomalous dimension. + r""" + Compute the leading-order non-singlet anomalous dimension. Implements Eq. (3.4) of :cite:`Moch:2004pa`. @@ -33,8 +33,8 @@ def gamma_ns(N, s1): @nb.njit(cache=True) def gamma_qg(N, nf): - """ - Computes the leading-order quark-gluon anomalous dimension + r""" + Compute the leading-order quark-gluon anomalous dimension. Implements Eq. (3.5) of :cite:`Vogt:2004mw`. @@ -57,8 +57,8 @@ def gamma_qg(N, nf): @nb.njit(cache=True) def gamma_gq(N): - """ - Computes the leading-order gluon-quark anomalous dimension + r""" + Compute the leading-order gluon-quark anomalous dimension. Implements Eq. (3.5) of :cite:`Vogt:2004mw`. @@ -79,8 +79,8 @@ def gamma_gq(N): @nb.njit(cache=True) def gamma_gg(N, s1, nf): - """ - Computes the leading-order gluon-gluon anomalous dimension + r""" + Compute the leading-order gluon-gluon anomalous dimension. Implements Eq. (3.5) of :cite:`Vogt:2004mw`. @@ -106,34 +106,34 @@ def gamma_gg(N, s1, nf): @nb.njit(cache=True) def gamma_singlet(N, s1, nf): r""" - Computes the leading-order singlet anomalous dimension matrix - - .. math:: - \gamma_S^{(0)} = \left(\begin{array}{cc} - \gamma_{qq}^{(0)} & \gamma_{qg}^{(0)}\\ - \gamma_{gq}^{(0)} & \gamma_{gg}^{(0)} - \end{array}\right) - - Parameters - ---------- - N : complex - Mellin moment - s1 : complex - harmonic sum :math:`S_{1}` - nf : int - Number of active flavors - - Returns - ------- - gamma_S_0 : numpy.ndarray - Leading-order singlet anomalous dimension matrix :math:`\gamma_{S}^{(0)}(N)` - - See Also - -------- - gamma_ns : :math:`\gamma_{qq}^{(0)}` - gamma_qg : :math:`\gamma_{qg}^{(0)}` - gamma_gq : :math:`\gamma_{gq}^{(0)}` - gamma_gg : :math:`\gamma_{gg}^{(0)}` + Compute the leading-order singlet anomalous dimension matrix. + + .. math:: + \gamma_S^{(0)} = \left(\begin{array}{cc} + \gamma_{qq}^{(0)} & \gamma_{qg}^{(0)}\\ + \gamma_{gq}^{(0)} & \gamma_{gg}^{(0)} + \end{array}\right) + + Parameters + ---------- + N : complex + Mellin moment + s1 : complex + harmonic sum :math:`S_{1}` + nf : int + Number of active flavors + + Returns + ------- + gamma_S_0 : numpy.ndarray + Leading-order singlet anomalous dimension matrix :math:`\gamma_{S}^{(0)}(N)` + + See Also + -------- + gamma_ns : :math:`\gamma_{qq}^{(0)}` + gamma_qg : :math:`\gamma_{qg}^{(0)}` + gamma_gq : :math:`\gamma_{gq}^{(0)}` + gamma_gg : :math:`\gamma_{gg}^{(0)}` """ gamma_qq = gamma_ns(N, s1) gamma_S_0 = np.array( @@ -145,56 +145,78 @@ def gamma_singlet(N, s1, nf): @nb.njit(cache=True) def gamma_QEDsinglet(N, s1, nf): r""" - Computes the leading-order singlet anomalous dimension matrix - - .. math:: - \gamma_S^{(0)} = \left(\begin{array}{cc} - \gamma_{qq}^{(0)} & \gamma_{qg}^{(0)}\\ - \gamma_{gq}^{(0)} & \gamma_{gg}^{(0)} - \end{array}\right) - - Parameters - ---------- - N : complex - Mellin moment - s1 : complex - harmonic sum :math:`S_{1}` - nf : int - Number of active flavors - - Returns - ------- - gamma_S_0 : numpy.ndarray - Leading-order singlet anomalous dimension matrix :math:`\gamma_{S}^{(0)}(N)` - - See Also - -------- - gamma_ns : :math:`\gamma_{qq}^{(0)}` - gamma_qg : :math:`\gamma_{qg}^{(0)}` - gamma_gq : :math:`\gamma_{gq}^{(0)}` - gamma_gg : :math:`\gamma_{gg}^{(0)}` + Compute the leading-order singlet anomalous dimension matrix. + + .. math:: + \gamma_S^{(0)} = \left(\begin{array}{cc} + \gamma_{qq}^{(0)} & \gamma_{qg}^{(0)}\\ + \gamma_{gq}^{(0)} & \gamma_{gg}^{(0)} + \end{array}\right) + + Parameters + ---------- + N : complex + Mellin moment + s1 : complex + harmonic sum :math:`S_{1}` + nf : int + Number of active flavors + + Returns + ------- + gamma_S_0 : numpy.ndarray + Leading-order singlet anomalous dimension matrix :math:`\gamma_{S}^{(0)}(N)` + + See Also + -------- + gamma_ns : :math:`\gamma_{qq}^{(0)}` + gamma_qg : :math:`\gamma_{qg}^{(0)}` + gamma_gq : :math:`\gamma_{gq}^{(0)}` + gamma_gg : :math:`\gamma_{gg}^{(0)}` """ gamma_qq = gamma_ns(N, s1) - gamma_S = np.zeros((4, 4), np.complex_) - gamma_S[0, 0] = gamma_gg(N, s1, nf) - gamma_S[0, 2] = gamma_gq(N) - gamma_S[2, 0] = gamma_qg(N, nf) - gamma_S[2, 2] = gamma_qq - gamma_S[3, 3] = gamma_qq - # gamma_S = np.array( - # [ - # [gamma_gg(N, s1, nf), 0.0, gamma_gq(N), 0.0], - # [0.0, 0.0, 0.0, 0.0], - # [gamma_qg(N, nf), 0.0, gamma_qq, 0.0], - # [0.0, 0.0, 0.0, gamma_qq], - # ], - # np.complex_, - # ) + gamma_S = np.array( + [ + [gamma_gg(N, s1, nf), 0.0 + 0.0j, gamma_gq(N), 0.0 + 0.0j], + [0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j], + [gamma_qg(N, nf), 0.0 + 0.0j, gamma_qq, 0.0 + 0.0j], + [0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, gamma_qq], + ], + np.complex_, + ) return gamma_S @nb.njit(cache=True) def gamma_QEDvalence(N, s1): + r""" + Compute the leading-order valence anomalous dimension matrix. + + .. math:: + \gamma_S^{(0)} = \left(\begin{array}{cc} + \gamma_{qq}^{(0)} & \gamma_{qg}^{(0)}\\ + \gamma_{gq}^{(0)} & \gamma_{gg}^{(0)} + \end{array}\right) + + Parameters + ---------- + N : complex + Mellin moment + s1 : complex + harmonic sum :math:`S_{1}` + + Returns + ------- + gamma_S_0 : numpy.ndarray + Leading-order singlet anomalous dimension matrix :math:`\gamma_{S}^{(0)}(N)` + + See Also + -------- + gamma_ns : :math:`\gamma_{qq}^{(0)}` + gamma_qg : :math:`\gamma_{qg}^{(0)}` + gamma_gq : :math:`\gamma_{gq}^{(0)}` + gamma_gg : :math:`\gamma_{gg}^{(0)}` + """ gamma_V = np.array( [ [1.0, 0.0], diff --git a/src/eko/anomalous_dimensions/as2.py b/src/eko/anomalous_dimensions/as2.py index eef893771..7642f4673 100644 --- a/src/eko/anomalous_dimensions/as2.py +++ b/src/eko/anomalous_dimensions/as2.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- -""" -This file contains the |NLO| Altarelli-Parisi splitting kernels. +"""Compute the |NLO| Altarelli-Parisi splitting kernels. These expression have been obtained using the procedure described in the `wiki `_ @@ -16,8 +15,8 @@ @nb.njit(cache=True) def gamma_nsm(n, nf, sx): - """ - Computes the |NLO| valence-like non-singlet anomalous dimension. + r""" + Compute the |NLO| valence-like non-singlet anomalous dimension. Implements Eq. (3.5) of :cite:`Moch:2004pa`. @@ -59,8 +58,8 @@ def gamma_nsm(n, nf, sx): @nb.njit(cache=True) def gamma_nsp(n, nf, sx): - """ - Computes the |NLO| singlet-like non-singlet anomalous dimension. + r""" + Compute the |NLO| singlet-like non-singlet anomalous dimension. Implements Eq. (3.5) of :cite:`Moch:2004pa`. @@ -100,8 +99,8 @@ def gamma_nsp(n, nf, sx): @nb.njit(cache=True) def gamma_ps(n, nf): - """ - Computes the |NLO| pure-singlet quark-quark anomalous dimension. + r""" + Compute the |NLO| pure-singlet quark-quark anomalous dimension. Implements Eq. (3.6) of :cite:`Vogt:2004mw`. @@ -127,8 +126,8 @@ def gamma_ps(n, nf): @nb.njit(cache=True) def gamma_qg(n, nf, sx): - """ - Computes the |NLO| quark-gluon singlet anomalous dimension. + r""" + Compute the |NLO| quark-gluon singlet anomalous dimension. Implements Eq. (3.7) of :cite:`Vogt:2004mw`. @@ -162,8 +161,8 @@ def gamma_qg(n, nf, sx): @nb.njit(cache=True) def gamma_gq(n, nf, sx): - """ - Computes the |NLO| gluon-quark singlet anomalous dimension. + r""" + Compute the |NLO| gluon-quark singlet anomalous dimension. Implements Eq. (3.8) of :cite:`Vogt:2004mw`. @@ -200,8 +199,8 @@ def gamma_gq(n, nf, sx): @nb.njit(cache=True) def gamma_gg(n, nf, sx): - """ - Computes the |NLO| gluon-gluon singlet anomalous dimension. + r""" + Compute the |NLO| gluon-gluon singlet anomalous dimension. Implements Eq. (3.9) of :cite:`Vogt:2004mw`. @@ -239,35 +238,35 @@ def gamma_gg(n, nf, sx): @nb.njit(cache=True) def gamma_singlet(n, nf, sx): r""" - Computes the next-leading-order singlet anomalous dimension matrix - - .. math:: - \gamma_S^{(1)} = \left(\begin{array}{cc} - \gamma_{qq}^{(1)} & \gamma_{qg}^{(1)}\\ - \gamma_{gq}^{(1)} & \gamma_{gg}^{(1)} - \end{array}\right) - - Parameters - ---------- - N : complex - Mellin moment - sx : numpy.ndarray - List of harmonic sums: :math:`S_{1},S_{2}` - nf : int - Number of active flavors - - Returns - ------- - gamma_S_1 : numpy.ndarray - |NLO| singlet anomalous dimension matrix :math:`\gamma_{S}^{(1)}(N)` - - See Also - -------- - gamma_nsp : :math:`\gamma_{qq}^{(1)}` - gamma_ps : :math:`\gamma_{qq}^{(1)}` - gamma_qg : :math:`\gamma_{qg}^{(1)}` - gamma_gq : :math:`\gamma_{gq}^{(1)}` - gamma_gg : :math:`\gamma_{gg}^{(1)}` + Compute the next-leading-order singlet anomalous dimension matrix. + + .. math:: + \gamma_S^{(1)} = \left(\begin{array}{cc} + \gamma_{qq}^{(1)} & \gamma_{qg}^{(1)}\\ + \gamma_{gq}^{(1)} & \gamma_{gg}^{(1)} + \end{array}\right) + + Parameters + ---------- + N : complex + Mellin moment + sx : numpy.ndarray + List of harmonic sums: :math:`S_{1},S_{2}` + nf : int + Number of active flavors + + Returns + ------- + gamma_S_1 : numpy.ndarray + |NLO| singlet anomalous dimension matrix :math:`\gamma_{S}^{(1)}(N)` + + See Also + -------- + gamma_nsp : :math:`\gamma_{qq}^{(1)}` + gamma_ps : :math:`\gamma_{qq}^{(1)}` + gamma_qg : :math:`\gamma_{qg}^{(1)}` + gamma_gq : :math:`\gamma_{gq}^{(1)}` + gamma_gg : :math:`\gamma_{gg}^{(1)}` """ gamma_qq = gamma_nsp(n, nf, sx) + gamma_ps(n, nf) gamma_S_0 = np.array( @@ -280,56 +279,78 @@ def gamma_singlet(n, nf, sx): @nb.njit(cache=True) def gamma_QEDsinglet(N, nf, sx): r""" - Computes the leading-order singlet anomalous dimension matrix - - .. math:: - \gamma_S^{(0)} = \left(\begin{array}{cc} - \gamma_{qq}^{(0)} & \gamma_{qg}^{(0)}\\ - \gamma_{gq}^{(0)} & \gamma_{gg}^{(0)} - \end{array}\right) - - Parameters - ---------- - N : complex - Mellin moment - s1 : complex - harmonic sum :math:`S_{1}` - nf : int - Number of active flavors - - Returns - ------- - gamma_S_0 : numpy.ndarray - Leading-order singlet anomalous dimension matrix :math:`\gamma_{S}^{(0)}(N)` - - See Also - -------- - gamma_ns : :math:`\gamma_{qq}^{(0)}` - gamma_qg : :math:`\gamma_{qg}^{(0)}` - gamma_gq : :math:`\gamma_{gq}^{(0)}` - gamma_gg : :math:`\gamma_{gg}^{(0)}` + Compute the leading-order singlet anomalous dimension matrix. + + .. math:: + \gamma_S^{(0)} = \left(\begin{array}{cc} + \gamma_{qq}^{(0)} & \gamma_{qg}^{(0)}\\ + \gamma_{gq}^{(0)} & \gamma_{gg}^{(0)} + \end{array}\right) + + Parameters + ---------- + N : complex + Mellin moment + s1 : complex + harmonic sum :math:`S_{1}` + nf : int + Number of active flavors + + Returns + ------- + gamma_S_0 : numpy.ndarray + Leading-order singlet anomalous dimension matrix :math:`\gamma_{S}^{(0)}(N)` + + See Also + -------- + gamma_ns : :math:`\gamma_{qq}^{(0)}` + gamma_qg : :math:`\gamma_{qg}^{(0)}` + gamma_gq : :math:`\gamma_{gq}^{(0)}` + gamma_gg : :math:`\gamma_{gg}^{(0)}` """ gamma_qq = gamma_nsp(N, nf, sx) + gamma_ps(N, nf) - gamma_S = np.zeros((4, 4), np.complex_) - gamma_S[0, 0] = gamma_gg(N, nf, sx) - gamma_S[0, 2] = gamma_gq(N, nf, sx) - gamma_S[2, 0] = gamma_qg(N, nf, sx) - gamma_S[2, 2] = gamma_qq - gamma_S[3, 3] = gamma_nsp(N, nf, sx) - # gamma_S = np.array( - # [ - # [gamma_gg(N, nf, sx), 0.0, gamma_gq(N, nf, sx), 0.0], - # [0.0, 0.0, 0.0, 0.0], - # [gamma_qg(N, nf, sx), 0.0, gamma_qq, 0.0], - # [0.0, 0.0, 0.0, gamma_nsp(N, nf, sx)], - # ], - # np.complex_, - # ) + gamma_S = np.array( + [ + [gamma_gg(N, nf, sx), 0.0 + 0.0j, gamma_gq(N, nf, sx), 0.0 + 0.0j], + [0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j], + [gamma_qg(N, nf, sx), 0.0 + 0.0j, gamma_qq, 0.0 + 0.0j], + [0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, gamma_nsp(N, nf, sx)], + ], + np.complex_, + ) return gamma_S @nb.njit(cache=True) def gamma_QEDvalence(N, nf, sx): + r""" + Compute the leading-order valence anomalous dimension matrix. + + .. math:: + \gamma_S^{(0)} = \left(\begin{array}{cc} + \gamma_{qq}^{(0)} & \gamma_{qg}^{(0)}\\ + \gamma_{gq}^{(0)} & \gamma_{gg}^{(0)} + \end{array}\right) + + Parameters + ---------- + N : complex + Mellin moment + s1 : complex + harmonic sum :math:`S_{1}` + + Returns + ------- + gamma_S_0 : numpy.ndarray + Leading-order singlet anomalous dimension matrix :math:`\gamma_{S}^{(0)}(N)` + + See Also + -------- + gamma_ns : :math:`\gamma_{qq}^{(0)}` + gamma_qg : :math:`\gamma_{qg}^{(0)}` + gamma_gq : :math:`\gamma_{gq}^{(0)}` + gamma_gg : :math:`\gamma_{gg}^{(0)}` + """ gamma_V = np.array( [ [1.0, 0.0], diff --git a/src/eko/anomalous_dimensions/as3.py b/src/eko/anomalous_dimensions/as3.py index 6d53e6aba..e18d73569 100644 --- a/src/eko/anomalous_dimensions/as3.py +++ b/src/eko/anomalous_dimensions/as3.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ -This file contains the |NNLO| Altarelli-Parisi splitting kernels. +Compute the |NNLO| Altarelli-Parisi splitting kernels. The expression have been obtained from :cite:`Moch:2004pa,Vogt:2004ns`. @@ -14,8 +14,8 @@ @nb.njit(cache=True) def gamma_nsm(n, nf, sx): - """ - Computes the |NNLO| valence-like non-singlet anomalous dimension. + r""" + Compute the |NNLO| valence-like non-singlet anomalous dimension. Implements Eq. (3.8) of :cite:`Moch:2004pa`. @@ -91,8 +91,8 @@ def gamma_nsm(n, nf, sx): @nb.njit(cache=True) def gamma_nsp(n, nf, sx): - """ - Computes the |NNLO| singlet-like non-singlet anomalous dimension. + r""" + Compute the |NNLO| singlet-like non-singlet anomalous dimension. Implements Eq. (3.7) of :cite:`Moch:2004pa`. @@ -168,8 +168,8 @@ def gamma_nsp(n, nf, sx): @nb.njit(cache=True) def gamma_nsv(n, nf, sx): - """ - Computes the |NNLO| valence non-singlet anomalous dimension. + r""" + Compute the |NNLO| valence non-singlet anomalous dimension. Implements Eq. (3.9) of :cite:`Moch:2004pa`. @@ -223,8 +223,8 @@ def gamma_nsv(n, nf, sx): @nb.njit(cache=True) def gamma_ps(n, nf, sx): - """ - Computes the |NNLO| pure-singlet quark-quark anomalous dimension. + r""" + Compute the |NNLO| pure-singlet quark-quark anomalous dimension. Implements Eq. (3.10) of :cite:`Vogt:2004mw`. @@ -295,8 +295,8 @@ def gamma_ps(n, nf, sx): @nb.njit(cache=True) def gamma_qg(n, nf, sx): - """ - Computes the |NNLO| quark-gluon singlet anomalous dimension. + r""" + Compute the |NNLO| quark-gluon singlet anomalous dimension. Implements Eq. (3.11) of :cite:`Vogt:2004mw`. @@ -369,8 +369,8 @@ def gamma_qg(n, nf, sx): @nb.njit(cache=True) def gamma_gq(n, nf, sx): - """ - Computes the |NNLO| gluon-quark singlet anomalous dimension. + r""" + Compute the |NNLO| gluon-quark singlet anomalous dimension. Implements Eq. (3.12) of :cite:`Vogt:2004mw`. @@ -459,8 +459,8 @@ def gamma_gq(n, nf, sx): @nb.njit(cache=True) def gamma_gg(n, nf, sx): - """ - Computes the |NNLO| gluon-gluon singlet anomalous dimension. + r""" + Compute the |NNLO| gluon-gluon singlet anomalous dimension. Implements Eq. (3.13) of :cite:`Vogt:2004mw`. @@ -548,37 +548,37 @@ def gamma_gg(n, nf, sx): @nb.njit(cache=True) def gamma_singlet(N, nf, sx): r""" - Computes the |NNLO| singlet anomalous dimension matrix - - .. math:: - \gamma_S^{(2)} = \left(\begin{array}{cc} - \gamma_{qq}^{(2)} & \gamma_{qg}^{(2)}\\ - \gamma_{gq}^{(2)} & \gamma_{gg}^{(2)} - \end{array}\right) - - Parameters - ---------- - N : complex - Mellin moment - nf : int - Number of active flavors - sx : np.ndarray - List of harmonic sums: :math:`S_{1},S_{2},S_{3}` + Compute the |NNLO| singlet anomalous dimension matrix. + .. math:: + \gamma_S^{(2)} = \left(\begin{array}{cc} + \gamma_{qq}^{(2)} & \gamma_{qg}^{(2)}\\ + \gamma_{gq}^{(2)} & \gamma_{gg}^{(2)} + \end{array}\right) + + Parameters + ---------- + N : complex + Mellin moment + nf : int + Number of active flavors + sx : np.ndarray + List of harmonic sums: :math:`S_{1},S_{2},S_{3}` - Returns - ------- - gamma_S_2 : numpy.ndarray - |NNLO| singlet anomalous dimension matrix - :math:`\gamma_{S}^{(2)}(N)` - See Also - -------- - gamma_nsp : :math:`\gamma_{ns,+}^{(2)}` - gamma_ps : :math:`\gamma_{ps}^{(2)}` - gamma_qg : :math:`\gamma_{qg}^{(2)}` - gamma_gq : :math:`\gamma_{gq}^{(2)}` - gamma_gg : :math:`\gamma_{gg}^{(2)}` + Returns + ------- + gamma_S_2 : numpy.ndarray + |NNLO| singlet anomalous dimension matrix + :math:`\gamma_{S}^{(2)}(N)` + + See Also + -------- + gamma_nsp : :math:`\gamma_{ns,+}^{(2)}` + gamma_ps : :math:`\gamma_{ps}^{(2)}` + gamma_qg : :math:`\gamma_{qg}^{(2)}` + gamma_gq : :math:`\gamma_{gq}^{(2)}` + gamma_gg : :math:`\gamma_{gg}^{(2)}` """ gamma_qq = gamma_nsp(N, nf, sx) + gamma_ps(N, nf, sx) gamma_S_0 = np.array( @@ -591,56 +591,78 @@ def gamma_singlet(N, nf, sx): @nb.njit(cache=True) def gamma_QEDsinglet(N, nf, sx): r""" - Computes the leading-order singlet anomalous dimension matrix - - .. math:: - \gamma_S^{(0)} = \left(\begin{array}{cc} - \gamma_{qq}^{(0)} & \gamma_{qg}^{(0)}\\ - \gamma_{gq}^{(0)} & \gamma_{gg}^{(0)} - \end{array}\right) - - Parameters - ---------- - N : complex - Mellin moment - s1 : complex - harmonic sum :math:`S_{1}` - nf : int - Number of active flavors - - Returns - ------- - gamma_S_0 : numpy.ndarray - Leading-order singlet anomalous dimension matrix :math:`\gamma_{S}^{(0)}(N)` - - See Also - -------- - gamma_ns : :math:`\gamma_{qq}^{(0)}` - gamma_qg : :math:`\gamma_{qg}^{(0)}` - gamma_gq : :math:`\gamma_{gq}^{(0)}` - gamma_gg : :math:`\gamma_{gg}^{(0)}` + Compute the leading-order singlet anomalous dimension matrix. + + .. math:: + \gamma_S^{(0)} = \left(\begin{array}{cc} + \gamma_{qq}^{(0)} & \gamma_{qg}^{(0)}\\ + \gamma_{gq}^{(0)} & \gamma_{gg}^{(0)} + \end{array}\right) + + Parameters + ---------- + N : complex + Mellin moment + s1 : complex + harmonic sum :math:`S_{1}` + nf : int + Number of active flavors + + Returns + ------- + gamma_S_0 : numpy.ndarray + Leading-order singlet anomalous dimension matrix :math:`\gamma_{S}^{(0)}(N)` + + See Also + -------- + gamma_ns : :math:`\gamma_{qq}^{(0)}` + gamma_qg : :math:`\gamma_{qg}^{(0)}` + gamma_gq : :math:`\gamma_{gq}^{(0)}` + gamma_gg : :math:`\gamma_{gg}^{(0)}` """ gamma_qq = gamma_nsp(N, nf, sx) + gamma_ps(N, nf, sx) - gamma_S = np.zeros((4, 4), np.complex_) - gamma_S[0, 0] = gamma_gg(N, nf, sx) - gamma_S[0, 2] = gamma_gq(N, nf, sx) - gamma_S[2, 0] = gamma_qg(N, nf, sx) - gamma_S[2, 2] = gamma_qq - gamma_S[3, 3] = gamma_nsp(N, nf, sx) - # gamma_S = np.array( - # [ - # [gamma_gg(N, nf, sx), 0.0, gamma_gq(N, nf, sx), 0.0], - # [0.0, 0.0, 0.0, 0.0], - # [gamma_qg(N, nf, sx), 0.0, gamma_qq, 0.0], - # [0.0, 0.0, 0.0, gamma_nsp(N, nf, sx)], - # ], - # np.complex_, - # ) + gamma_S = np.array( + [ + [gamma_gg(N, nf, sx), 0.0 + 0.0j, gamma_gq(N, nf, sx), 0.0 + 0.0j], + [0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j], + [gamma_qg(N, nf, sx), 0.0 + 0.0j, gamma_qq, 0.0 + 0.0j], + [0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, gamma_nsp(N, nf, sx)], + ], + np.complex_, + ) return gamma_S @nb.njit(cache=True) def gamma_QEDvalence(N, nf, sx): + r""" + Compute the leading-order valence anomalous dimension matrix. + + .. math:: + \gamma_S^{(0)} = \left(\begin{array}{cc} + \gamma_{qq}^{(0)} & \gamma_{qg}^{(0)}\\ + \gamma_{gq}^{(0)} & \gamma_{gg}^{(0)} + \end{array}\right) + + Parameters + ---------- + N : complex + Mellin moment + s1 : complex + harmonic sum :math:`S_{1}` + + Returns + ------- + gamma_S_0 : numpy.ndarray + Leading-order singlet anomalous dimension matrix :math:`\gamma_{S}^{(0)}(N)` + + See Also + -------- + gamma_ns : :math:`\gamma_{qq}^{(0)}` + gamma_qg : :math:`\gamma_{qg}^{(0)}` + gamma_gq : :math:`\gamma_{gq}^{(0)}` + gamma_gg : :math:`\gamma_{gg}^{(0)}` + """ gamma_V = np.array( [ [gamma_nsv(N, nf, sx), 0.0], From 6e8b1ab1309c1dbe70f3f285086eee0a46cec738 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 20 Sep 2022 09:40:58 +0200 Subject: [PATCH 147/312] Modify NNPDF_bench.py --- benchmarks/NNPDF_bench.py | 3 ++- src/eko/anomalous_dimensions/aem1.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/benchmarks/NNPDF_bench.py b/benchmarks/NNPDF_bench.py index daacb0682..6eba3435b 100644 --- a/benchmarks/NNPDF_bench.py +++ b/benchmarks/NNPDF_bench.py @@ -76,6 +76,7 @@ def benchmark_nnlo(self, Q0=1.65, Q2grid=(100,)): theory_card.update( { "ModEv": "iterate-exact", + "FNS": "VFNS", } ) @@ -127,7 +128,7 @@ def benchmark_nnlo(self, Q0=1.65, Q2grid=(100,)): # # test backward # #nn31.benchmark_nlo(Q0=np.sqrt(high2), Q2grid=[low2]) nn31qed = BenchmarkNNPDF31_luxqed() - nn31qed.benchmark_nnlo(Q0=30.0, Q2grid=(10000,)) + nn31qed.benchmark_nnlo() # nn40 = BenchmarkNNPDF40() # nn40.benchmark_nnlo(Q2grid=[100]) # nn40.benchmark_nnlo(Q0=np.sqrt(high2), Q2grid=[low2]) diff --git a/src/eko/anomalous_dimensions/aem1.py b/src/eko/anomalous_dimensions/aem1.py index 1696b2414..6387aae9a 100644 --- a/src/eko/anomalous_dimensions/aem1.py +++ b/src/eko/anomalous_dimensions/aem1.py @@ -130,9 +130,9 @@ def gamma_singlet(N, nf, sx): gamma_gg : :math:`\gamma_{gg}^{(0)}` """ e2avg = constants.e2avg(nf) - e2delta = constants.vde2m(nf) - constants.vue2m(nf) + constants.e2avg(nf) vue2m = constants.vue2m(nf) vde2m = constants.vde2m(nf) + e2delta = vde2m - vue2m + e2avg gamma_S_01 = np.array( [ [0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j], From c07a60732ca6b5d6e0727a933006d6ddc8658bb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 20 Sep 2022 15:23:02 +0200 Subject: [PATCH 148/312] Make numbers explicitly floats --- src/eko/anomalous_dimensions/aem1.py | 2 +- src/eko/anomalous_dimensions/aem2.py | 60 ++++----- src/eko/anomalous_dimensions/as1aem1.py | 159 +++++++++++++----------- 3 files changed, 120 insertions(+), 101 deletions(-) diff --git a/src/eko/anomalous_dimensions/aem1.py b/src/eko/anomalous_dimensions/aem1.py index 6387aae9a..b410489b1 100644 --- a/src/eko/anomalous_dimensions/aem1.py +++ b/src/eko/anomalous_dimensions/aem1.py @@ -71,7 +71,7 @@ def gamma_phph(nf): """ nu = constants.uplike_flavors(nf) nd = nf - nu - return 4 / 3 * constants.NC * (nu * constants.eu2 + nd * constants.ed2) + return 4.0 / 3.0 * constants.NC * (nu * constants.eu2 + nd * constants.ed2) @nb.njit(cache=True) diff --git a/src/eko/anomalous_dimensions/aem2.py b/src/eko/anomalous_dimensions/aem2.py index 357bdc515..bfcd3f990 100644 --- a/src/eko/anomalous_dimensions/aem2.py +++ b/src/eko/anomalous_dimensions/aem2.py @@ -109,10 +109,10 @@ def gamma_phu(N, nf, sx): nu = constants.uplike_flavors(nf) nd = nf - nu S1 = sx[0] - tmp = (-16 * (-16 - 27 * N - 13 * N**2 - 8 * N**3)) / ( - 9.0 * (-1 + N) * N * (1 + N) ** 2 - ) - 16 * (2 + 3 * N + 2 * N**2 + N**3) / ( - 3.0 * (-1 + N) * N * (1 + N) ** 2 + tmp = (-16.0 * (-16.0 - 27.0 * N - 13.0 * N**2 - 8.0 * N**3)) / ( + 9.0 * (-1.0 + N) * N * (1.0 + N) ** 2 + ) - 16.0 * (2.0 + 3.0 * N + 2.0 * N**2 + N**3) / ( + 3.0 * (-1.0 + N) * N * (1.0 + N) ** 2 ) * S1 eSigma2 = constants.NC * (nu * constants.eu2 + nd * constants.ed2) return constants.eu2 * as1aem1.gamma_phq(N, sx) / constants.CF + eSigma2 * tmp @@ -142,10 +142,10 @@ def gamma_phd(N, nf, sx): nu = constants.uplike_flavors(nf) nd = nf - nu S1 = sx[0] - tmp = (-16 * (-16 - 27 * N - 13 * N**2 - 8 * N**3)) / ( - 9.0 * (-1 + N) * N * (1 + N) ** 2 - ) - 16 * (2 + 3 * N + 2 * N**2 + N**3) / ( - 3.0 * (-1 + N) * N * (1 + N) ** 2 + tmp = (-16.0 * (-16.0 - 27.0 * N - 13.0 * N**2 - 8.0 * N**3)) / ( + 9.0 * (-1.0 + N) * N * (1.0 + N) ** 2 + ) - 16.0 * (2.0 + 3.0 * N + 2.0 * N**2 + N**3) / ( + 3.0 * (-1.0 + N) * N * (1.0 + N) ** 2 ) * S1 eSigma2 = constants.NC * (nu * constants.eu2 + nd * constants.ed2) return constants.ed2 * as1aem1.gamma_phq(N, sx) / constants.CF + eSigma2 * tmp @@ -179,13 +179,13 @@ def gamma_nspu(N, nf, sx): nd = nf - nu eSigma2 = constants.NC * (nu * constants.eu2 + nd * constants.ed2) tmp = ( - 2 - * (-12 + 20 * N + 47 * N**2 + 6 * N**3 + 3 * N**4) - / (9.0 * N**2 * (1 + N) ** 2) - - 80 / 9 * S1 - + 16 / 3 * S2 + 2.0 + * (-12.0 + 20.0 * N + 47.0 * N**2 + 6.0 * N**3 + 3.0 * N**4) + / (9.0 * N**2 * (1.0 + N) ** 2) + - 80.0 / 9.0 * S1 + + 16.0 / 3.0 * S2 ) * eSigma2 - return constants.eu2 * as1aem1.gamma_nsp(N, sx) / constants.CF / 2 + tmp + return constants.eu2 * as1aem1.gamma_nsp(N, sx) / constants.CF / 2.0 + tmp @nb.njit(cache=True) @@ -216,13 +216,13 @@ def gamma_nspd(N, nf, sx): nd = nf - nu eSigma2 = constants.NC * (nu * constants.eu2 + nd * constants.ed2) tmp = ( - 2 - * (-12 + 20 * N + 47 * N**2 + 6 * N**3 + 3 * N**4) - / (9.0 * N**2 * (1 + N) ** 2) - - 80 / 9 * S1 - + 16 / 3 * S2 + 2.0 + * (-12.0 + 20.0 * N + 47.0 * N**2 + 6.0 * N**3 + 3.0 * N**4) + / (9.0 * N**2 * (1.0 + N) ** 2) + - 80.0 / 9.0 * S1 + + 16.0 / 3.0 * S2 ) * eSigma2 - return constants.ed2 * as1aem1.gamma_nsp(N, sx) / constants.CF / 2 + tmp + return constants.ed2 * as1aem1.gamma_nsp(N, sx) / constants.CF / 2.0 + tmp @nb.njit(cache=True) @@ -290,13 +290,13 @@ def gamma_nsmd(N, nf, sx): nd = nf - nu eSigma2 = constants.NC * (nu * constants.eu2 + nd * constants.ed2) tmp = ( - 2 - * (-12 + 20 * N + 47 * N**2 + 6 * N**3 + 3 * N**4) - / (9.0 * N**2 * (1 + N) ** 2) - - 80 / 9 * S1 - + 16 / 3 * S2 + 2.0 + * (-12.0 + 20.0 * N + 47.0 * N**2 + 6.0 * N**3 + 3.0 * N**4) + / (9.0 * N**2 * (1.0 + N) ** 2) + - 80.0 / 9.0 * S1 + + 16.0 / 3.0 * S2 ) * eSigma2 - return constants.ed2 * as1aem1.gamma_nsm(N, sx) / constants.CF / 2 + tmp + return constants.ed2 * as1aem1.gamma_nsm(N, sx) / constants.CF / 2.0 + tmp @nb.njit(cache=True) @@ -320,10 +320,10 @@ def gamma_ps(N, nf): """ result = ( - -4 - * (2 + N * (5 + N)) - * (4 + N * (4 + N * (7 + 5 * N))) - / ((-1 + N) * N**3 * (1 + N) ** 3 * (2 + N) ** 2) + -4.0 + * (2.0 + N * (5.0 + N)) + * (4.0 + N * (4.0 + N * (7.0 + 5.0 * N))) + / ((-1.0 + N) * N**3 * (1.0 + N) ** 3 * (2.0 + N) ** 2) ) return 2 * nf * constants.CA * result diff --git a/src/eko/anomalous_dimensions/as1aem1.py b/src/eko/anomalous_dimensions/as1aem1.py index 940d03856..fdb8531e4 100644 --- a/src/eko/anomalous_dimensions/as1aem1.py +++ b/src/eko/anomalous_dimensions/as1aem1.py @@ -30,17 +30,25 @@ def gamma_phq(N, sx): S1 = sx[0] S2 = sx[1] tmp_const = ( - 2 - * (-4 - 12 * N - N**2 + 28 * N**3 + 43 * N**4 + 30 * N**5 + 12 * N**6) - / ((-1 + N) * N**3 * (1 + N) ** 3) + 2.0 + * ( + -4.0 + - 12.0 * N + - N**2 + + 28.0 * N**3 + + 43.0 * N**4 + + 30.0 * N**5 + + 12.0 * N**6 + ) + / ((-1.0 + N) * N**3 * (1.0 + N) ** 3) ) tmp_S1 = ( - -4 - * (10 + 27 * N + 25 * N**2 + 13 * N**3 + 5 * N**4) - / ((-1 + N) * N * (1 + N) ** 3) + -4.0 + * (10.0 + 27.0 * N + 25.0 * N**2 + 13.0 * N**3 + 5.0 * N**4) + / ((-1.0 + N) * N * (1.0 + N) ** 3) ) - tmp_S12 = 4 * (2 + N + N**2) / ((-1 + N) * N * (1 + N)) - tmp_S2 = 4 * (2 + N + N**2) / ((-1 + N) * N * (1 + N)) + tmp_S12 = 4.0 * (2.0 + N + N**2) / ((-1.0 + N) * N * (1.0 + N)) + tmp_S2 = 4.0 * (2.0 + N + N**2) / ((-1.0 + N) * N * (1.0 + N)) return constants.CF * (tmp_const + tmp_S1 * S1 + tmp_S12 * S1**2 + tmp_S2 * S2) @@ -69,23 +77,23 @@ def gamma_qph(N, nf, sx): S1 = sx[0] S2 = sx[1] tmp_const = ( - -2 + -2.0 * ( - 4 - + 8 * N - + 25 * N**2 - + 51 * N**3 - + 36 * N**4 - + 15 * N**5 - + 5 * N**6 + 4.0 + + 8.0 * N + + 25.0 * N**2 + + 51.0 * N**3 + + 36.0 * N**4 + + 15.0 * N**5 + + 5.0 * N**6 ) - / (N**3 * (1 + N) ** 3 * (2 + N)) + / (N**3 * (1.0 + N) ** 3 * (2.0 + N)) ) - tmp_S1 = 8 / N**2 - tmp_S12 = -4 * (2 + N + N**2) / (N * (1 + N) * (2 + N)) - tmp_S2 = 4 * (2 + N + N**2) / (N * (1 + N) * (2 + N)) + tmp_S1 = 8.0 / N**2 + tmp_S12 = -4.0 * (2.0 + N + N**2) / (N * (1.0 + N) * (2.0 + N)) + tmp_S2 = 4.0 * (2.0 + N + N**2) / (N * (1.0 + N) * (2.0 + N)) return ( - 2 + 2.0 * nf * constants.CA * constants.CF @@ -114,8 +122,14 @@ def gamma_gph(N): return ( constants.CF * constants.CA - * (8 * (-4 + N * (-4 + N * (-5 + N * (-10 + N + 2 * N**2 * (2 + N)))))) - / (N**3 * (1 + N) ** 3 * (-2 + N + N**2)) + * ( + 8.0 + * ( + -4.0 + + N * (-4.0 + N * (-5.0 + N * (-10.0 + N + 2.0 * N**2 * (2.0 + N)))) + ) + ) + / (N**3 * (1.0 + N) ** 3 * (-2.0 + N + N**2)) ) @@ -207,7 +221,7 @@ def gamma_phph(nf): """ nu = constants.uplike_flavors(nf) nd = nf - nu - return 4 * constants.CF * constants.CA * (nu * constants.eu2 + nd * constants.ed2) + return 4.0 * constants.CF * constants.CA * (nu * constants.eu2 + nd * constants.ed2) @nb.njit(cache=True) @@ -223,7 +237,7 @@ def gamma_gg(): :math:`\\gamma_{gg}^{(1,1)}(N)` """ - return 4 * constants.TR + return 4.0 * constants.TR @nb.njit(cache=True) @@ -249,42 +263,47 @@ def gamma_nsp(N, sx): S1 = sx[0] S2 = sx[1] S3 = sx[2] - S1h = harmonics.S1(N / 2) - S2h = harmonics.S2(N / 2) - S3h = harmonics.S3(N / 2) - S1p1h = harmonics.S1((N + 1.0) / 2) - S2p1h = harmonics.S2((N + 1) / 2) - S3p1h = harmonics.S3((N + 1) / 2) + S1h = harmonics.S1(N / 2.0) + S2h = harmonics.S2(N / 2.0) + S3h = harmonics.S3(N / 2.0) + S1p1h = harmonics.S1((N + 1.0) / 2.0) + S2p1h = harmonics.S2((N + 1.0) / 2.0) + S3p1h = harmonics.S3((N + 1.0) / 2.0) g3N = harmonics.g_functions.mellin_g3(N, S1) S1p2 = harmonics.polygamma.recursive_harmonic_sum(S1, N, 2, 1) - g3Np2 = harmonics.g_functions.mellin_g3(N + 2, S1p2) + g3Np2 = harmonics.g_functions.mellin_g3(N + 2.0, S1p2) result = ( - +32 * zeta2 * S1h - - 32 * zeta2 * S1p1h + +32.0 * zeta2 * S1h + - 32.0 * zeta2 * S1p1h + 8.0 / (N + N**2) * S2h - - 4 * S3h - + (24 + 16 / (N + N**2)) * S2 - - 32 * S3 + - 4.0 * S3h + + (24.0 + 16.0 / (N + N**2)) * S2 + - 32.0 * S3 - 8.0 / (N + N**2) * S2p1h + S1 * ( - +16 * (3 / N**2 - 3 / (1 + N) ** 2 + 2 * zeta2) - - 16 * S2h - - 32 * S2 - + 16 * S2p1h + +16.0 * (3.0 / N**2 - 3.0 / (1.0 + N) ** 2 + 2.0 * zeta2) + - 16.0 * S2h + - 32.0 * S2 + + 16.0 * S2p1h ) + ( - -8 + -8.0 + N * ( - -32 - + N * (-8 - 3 * N * (3 + N) * (3 + N**2) - 48 * (1 + N) ** 2 * zeta2) + -32.0 + + N + * ( + -8.0 + - 3.0 * N * (3.0 + N) * (3.0 + N**2) + - 48.0 * (1.0 + N) ** 2 * zeta2 + ) ) ) - / (N**3 * (1 + N) ** 3) - + 32 * (g3N + g3Np2) - + 4 * S3p1h - - 16 * zeta3 + / (N**3 * (1.0 + N) ** 3) + + 32.0 * (g3N + g3Np2) + + 4.0 * S3p1h + - 16.0 * zeta3 ) return constants.CF * result @@ -312,44 +331,44 @@ def gamma_nsm(N, sx): S1 = sx[0] S2 = sx[1] S3 = sx[2] - S1h = harmonics.S1(N / 2) - S2h = harmonics.S2(N / 2) - S3h = harmonics.S3(N / 2) - S1p1h = harmonics.S1((N + 1.0) / 2) - S2p1h = harmonics.S2((N + 1) / 2) - S3p1h = harmonics.S3((N + 1) / 2) + S1h = harmonics.S1(N / 2.0) + S2h = harmonics.S2(N / 2.0) + S3h = harmonics.S3(N / 2.0) + S1p1h = harmonics.S1((N + 1.0) / 2.0) + S2p1h = harmonics.S2((N + 1.0) / 2.0) + S3p1h = harmonics.S3((N + 1.0) / 2.0) g3N = harmonics.g_functions.mellin_g3(N, S1) S1p2 = harmonics.polygamma.recursive_harmonic_sum(S1, N, 2, 1) - g3Np2 = harmonics.g_functions.mellin_g3(N + 2, S1p2) + g3Np2 = harmonics.g_functions.mellin_g3(N + 2.0, S1p2) result = ( -32.0 * zeta2 * S1h - 8.0 / (N + N**2) * S2h - + (24 + 16 / (N + N**2)) * S2 + + (24.0 + 16.0 / (N + N**2)) * S2 + 8.0 / (N + N**2) * S2p1h + S1 * ( - 16 * (-1 / N**2 + 1 / (1 + N) ** 2 + 2 * zeta2) - + 16 * S2h - - 32 * S2 - - 16 * S2p1h + 16.0 * (-1.0 / N**2 + 1.0 / (1.0 + N) ** 2 + 2.0 * zeta2) + + 16.0 * S2h + - 32.0 * S2 + - 16.0 * S2p1h ) + ( - 72 + 72.0 + N * ( - 96 - - 3 * N * (8 + 3 * N * (3 + N) * (3 + N**2)) - + 48 * N * (1 + N) ** 2 * zeta2 + 96.0 + - 3.0 * N * (8.0 + 3.0 * N * (3.0 + N) * (3.0 + N**2)) + + 48.0 * N * (1.0 + N) ** 2 * zeta2 ) ) - / (3.0 * N**3 * (1 + N) ** 3) - - 32 * (g3N + g3Np2) + / (3.0 * N**3 * (1.0 + N) ** 3) + - 32.0 * (g3N + g3Np2) + 32.0 * zeta2 * S1p1h - + 4 * S3h - - 32 * S3 - - 4 * S3p1h - - 16 * zeta3 + + 4.0 * S3h + - 32.0 * S3 + - 4.0 * S3p1h + - 16.0 * zeta3 ) return constants.CF * result From 1539b15e5ffdc348e104185adbe69fd8244baa57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 21 Sep 2022 17:49:09 +0200 Subject: [PATCH 149/312] Fix integration mellin.Path for QEDsinglet --- src/eko/evolution_operator/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 31676dcb9..5823eb412 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -140,7 +140,7 @@ class QuadKerBase: def __init__(self, u, is_log, logx, mode0): self.is_singlet = mode0 in [100, 21, 90] # TODO : check 90 in is_QEDsinglet and 91 in is_QEDvalence - self.is_QEDsinglet = mode0 in [100, 101, 21, 22, 90] + self.is_QEDsinglet = mode0 in [21, 22, 100, 101, 90] self.is_QEDvalence = mode0 in [10200, 10204] self.is_log = is_log self.u = u @@ -149,7 +149,10 @@ def __init__(self, u, is_log, logx, mode0): @property def path(self): """Return the associated instance of :class:`eko.mellin.Path`.""" - return mellin.Path(self.u, self.logx, self.is_singlet) + if self.is_singlet or self.is_QEDsinglet: + return mellin.Path(self.u, self.logx, True) + else: + return mellin.Path(self.u, self.logx, False) @property def n(self): From dc7746d1d200ef91118f2bbb60081472347c69b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 22 Sep 2022 13:01:46 +0200 Subject: [PATCH 150/312] Add identity matching for the photon --- src/eko/matching_conditions/__init__.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/eko/matching_conditions/__init__.py b/src/eko/matching_conditions/__init__.py index c9140ecbd..742299309 100644 --- a/src/eko/matching_conditions/__init__.py +++ b/src/eko/matching_conditions/__init__.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -""" -This module defines the matching conditions for the |VFNS| evolution. -""" +"""Defines the matching conditions for the |VFNS| evolution.""" from .. import basis_rotation as br from .. import member @@ -41,13 +39,13 @@ def split_ad_to_evol_map( intrinsic_range : list list of intrinsic quark pids """ - m = { "S.S": op_members[(100, 100)], "S.g": op_members[(100, 21)], "g.S": op_members[(21, 100)], "g.g": op_members[(21, 21)], "V.V": op_members[(200, 200)], + "ph.ph": member.OpMember.id_like(op_members[(200, 200)]).copy(), } # add elements which are already active From 3bad6557afdabf0f16e18f507d1eeb063858b371 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 22 Sep 2022 14:38:06 +0200 Subject: [PATCH 151/312] Fix test_matching --- src/eko/matching_conditions/__init__.py | 2 +- tests/eko/test_matching.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/eko/matching_conditions/__init__.py b/src/eko/matching_conditions/__init__.py index 742299309..f1a036ead 100644 --- a/src/eko/matching_conditions/__init__.py +++ b/src/eko/matching_conditions/__init__.py @@ -45,7 +45,6 @@ def split_ad_to_evol_map( "g.S": op_members[(21, 100)], "g.g": op_members[(21, 21)], "V.V": op_members[(200, 200)], - "ph.ph": member.OpMember.id_like(op_members[(200, 200)]).copy(), } # add elements which are already active @@ -59,6 +58,7 @@ def split_ad_to_evol_map( { "Sdelta.Sdelta": op_members[(200, 200)], "Vdelta.Vdelta": op_members[(200, 200)], + "ph.ph": member.OpMember.id_like(op_members[(200, 200)]).copy(), } ) names = {3: "d3", 4: "u3", 5: "d8", 6: "u8"} diff --git a/tests/eko/test_matching.py b/tests/eko/test_matching.py index 81ecb6868..20a56d165 100644 --- a/tests/eko/test_matching.py +++ b/tests/eko/test_matching.py @@ -131,6 +131,7 @@ def test_split_ad_to_evol_map_qed(self): ome = self.mkOME() a = MatchingCondition.split_ad_to_evol_map(ome, 3, 1, [], qed=True) triv_keys = [ + "ph.ph", "S.S", "S.g", "g.S", From da31b4ae5b01d0e4c24c91161e59410c7f7a1a6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 22 Sep 2022 17:04:17 +0200 Subject: [PATCH 152/312] Bench against CT18qed --- benchmarks/CT18_bench.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/benchmarks/CT18_bench.py b/benchmarks/CT18_bench.py index 6aef16e85..714454cd4 100644 --- a/benchmarks/CT18_bench.py +++ b/benchmarks/CT18_bench.py @@ -54,6 +54,28 @@ def benchmark_nnlo(self, Q0=1.295, Q2grid=(1e4,)): ] self.run([theory_card], [operator_card], ["CT18NNLO"]) + def benchmark_nnlo_qed(self, Q0=1.295, Q2grid=(1e4,)): + theory_card = base_theory.copy() + theory_card.update( + { + "alphas": 0.118000, + "alphaqed": 0.007496, + "PTO": 2, + "QED": 1, + "Q0": Q0, + "MaxNfPdf": 5, + "MaxNfAs": 5, + } + ) + operator_card = {"Q2grid": list(Q2grid)} + self.skip_pdfs = lambda _theory: [ + -6, + 6, + "Tu8", + "Vu8", + ] + self.run([theory_card], [operator_card], ["CT18qed"]) + def benchmark_znnlo(self, Q0=1.3, Q2grid=(1e4,)): theory_card = base_theory.copy() theory_card.update( @@ -82,4 +104,4 @@ def benchmark_znnlo(self, Q0=1.3, Q2grid=(1e4,)): if __name__ == "__main__": b = BenchmarkCT18() - b.benchmark_nnlo() + b.benchmark_nnlo_qed() From a90b4347440881c02e568f0431f162bc1b59a1f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 23 Sep 2022 12:32:14 +0200 Subject: [PATCH 153/312] Implement fixed alpha running in couplings.py --- src/eko/couplings.py | 259 +++++++++++++++++++++++--------- tests/eko/test_couplings.py | 284 +++++++++++++++++++----------------- 2 files changed, 345 insertions(+), 198 deletions(-) diff --git a/src/eko/couplings.py b/src/eko/couplings.py index b6c0d00ea..73503831e 100644 --- a/src/eko/couplings.py +++ b/src/eko/couplings.py @@ -271,7 +271,7 @@ def expanded_qed(ref, order, nf, lmu): @nb.njit(cache=True) -def couplings_expanded(order, couplings_ref, nf, scale_from, scale_to): +def couplings_expanded_running_alphaem(order, couplings_ref, nf, scale_from, scale_to): r"""Compute expanded expression. Parameters @@ -296,7 +296,9 @@ def couplings_expanded(order, couplings_ref, nf, scale_from, scale_to): lmu = np.log(scale_to / scale_from) res_as = expanded_qcd(couplings_ref[0], order[0], nf, lmu) res_aem = expanded_qed(couplings_ref[1], order[1], nf, lmu) - if order[0] >= 2 and order[1] >= 2: + # if order[0] >= 1 and order[1] >= 1: + # order[0] is always >=1 + if order[1] >= 1: beta_qcd0 = beta_qcd((2, 0), nf) beta_qed0 = beta_qed((0, 2), nf) res_as += ( @@ -312,6 +314,63 @@ def couplings_expanded(order, couplings_ref, nf, scale_from, scale_to): return np.array([res_as, res_aem]) +@nb.njit(cache=True) +def couplings_expanded_fixed_alphaem(order, couplings_ref, nf, scale_from, scale_to): + r"""Compute expanded expression. + + Parameters + ---------- + order : tuple(int, int) + perturbation order + couplings_ref : numpy.ndarray + reference alpha_s and alpha + nf : int + value of nf for computing the couplings + scale_from : float + reference scale + scale_to : float + target scale + + Returns + ------- + numpy.ndarray + couplings at target scale :math:`a(\mu_R^2)` + """ + # common vars + lmu = np.log(scale_to / scale_from) + res_as = couplings_ref[0] + aem = couplings_ref[1] + beta_qcd0 = beta_qcd((2, 0), nf) + if order[1] >= 1: + beta_qcd0 += aem * beta_qcd((2, 1), nf) + # QCD LO + as_LO = exact_lo(couplings_ref[0], beta_qcd0, lmu) + res_as = as_LO + # NLO + if order[0] >= 2: + b_qcd1 = beta_qcd((3, 0), nf) / beta_qcd0 + as_NLO = expanded_nlo(couplings_ref[0], beta_qcd0, b_qcd1, lmu) + res_as = as_NLO + # NNLO + if order[0] >= 3: + b_qcd2 = beta_qcd((4, 0), nf) / beta_qcd0 + as_NNLO = expanded_nnlo(couplings_ref[0], beta_qcd0, b_qcd1, b_qcd2, lmu) + res_as = as_NNLO + # N3LO + if order[0] >= 4: + b_qcd3 = beta_qcd((5, 0), nf) / beta_qcd0 + as_N3LO = expanded_n3lo( + couplings_ref[0], + beta_qcd0, + b_qcd1, + b_qcd2, + b_qcd3, + lmu, + ) + res_as = as_N3LO + return np.array([res_as, aem]) + + class Couplings: r"""Compute the strong and electromagnetic coupling constants :math:`a_s, a_{em}`. @@ -367,6 +426,7 @@ def __init__( nf_ref=None, max_nf=None, hqm_scheme="POLE", + running_alphaem=False, ): # Sanity checks if couplings_ref[0] <= 0: @@ -381,10 +441,13 @@ def __init__( raise NotImplementedError("a_s beyond N3LO is not implemented") if order[1] not in [0, 1, 2]: raise NotImplementedError("a_em beyond NLO is not implemented") + if order[0] == 0: + raise ValueError("QCD evolution order must be at least 1 (PTO>=0)") self.order = order if method not in ["expanded", "exact"]: raise ValueError(f"Unknown method {method}") self.method = method + self.running_alphaem = running_alphaem # create new threshold object self.a_ref = couplings_ref.copy() / 4.0 / np.pi # convert to a_s and a_em @@ -416,7 +479,7 @@ def q2_ref(self): return self.thresholds.q2_ref @classmethod - def from_dict(cls, theory_card, masses=None): + def from_dict(cls, theory_card, masses=None, running_alphaem=False): r"""Create object from theory dictionary. Parameters @@ -466,9 +529,44 @@ def from_dict(cls, theory_card, masses=None): nf_ref, max_nf, hqm_scheme, + running_alphaem, ) - def compute_exact(self, a_ref, nf, scale_from, scale_to): + def unidimensional_exact(self, beta0, b_vec, u, a_ref, method, rtol): + """Compute single coupling via decoupled |RGE|. + + Parameters + ---------- + as_ref : float + reference alpha_s or alpha + nf : int + value of nf for computing alpha_i + scale_from : float + reference scale + scale_to : float + target scale + + Returns + ------- + float + coupling at target scale :math:`a(Q^2)` + """ + + def rge(_t, a, b_vec): + rge = -(a**2) * (np.sum([a**k * b for k, b in enumerate(b_vec)])) + return rge + + res = scipy.integrate.solve_ivp( + rge, + (0, beta0 * u), + (a_ref,), + args=[b_vec], + method=method, + rtol=rtol, + ) + return res.y[0][-1] + + def compute_exact_running_alphaem(self, a_ref, nf, scale_from, scale_to): """Compute couplings via |RGE|. Parameters @@ -490,75 +588,44 @@ def compute_exact(self, a_ref, nf, scale_from, scale_to): # in LO fallback to expanded, as this is the full solution u = np.log(scale_to / scale_from) - def unidimensional_exact(beta0, b_vec, u, a_ref, method, rtol): - def rge(_t, a, b_vec): - rge = -(a**2) * (np.sum([a**k * b for k, b in enumerate(b_vec)])) - return rge - - res = scipy.integrate.solve_ivp( - rge, - (0, beta0 * u), - (a_ref,), - args=[b_vec], - method=method, - rtol=rtol, - ) - return res.y[0][-1] - - if self.order in [(0, 0), (0, 1), (1, 0), (1, 1)]: - return couplings_expanded( + if self.order == (1, 0): + return couplings_expanded_fixed_alphaem( self.order, a_ref, nf, scale_from, float(scale_to) ) - if self.order[0] in [0, 1]: - # return expanded solution for a_s and exact for a_em - a_s = couplings_expanded( - self.order, a_ref, nf, scale_from, float(scale_to) - )[0] - beta0_qed = beta_qed((0, 2), nf) - b_qed_vec = [1.0] - # NLO - # if self.order[1] >= 2: # I think that at this point this if is always true - b_qed_vec.append(b_qed((0, 3), nf)) - a_em = unidimensional_exact( - beta0_qed, b_qed_vec, u, a_ref[1], "Radau", 1e-6 - ) - return np.array([a_s, a_em]) - if self.order[1] in [0, 1]: - # return expanded solution for a_em and exact for a_s - a_em = couplings_expanded( - self.order, a_ref, nf, scale_from, float(scale_to) - )[1] - beta0_qcd = beta_qcd((2, 0), nf) - b_qcd_vec = [1.0] - # NLO - if self.order[0] >= 2: - b_qcd_vec.append(b_qcd((3, 0), nf)) - # NNLO - if self.order[0] >= 3: - b_qcd_vec.append(b_qcd((4, 0), nf)) - # N3LO - if self.order[0] >= 4: - b_qcd_vec.append(b_qcd((5, 0), nf)) - a_s = unidimensional_exact(beta0_qcd, b_qcd_vec, u, a_ref[0], "Radau", 1e-6) - return np.array([a_s, a_em]) - # otherwise rescale the RGE to run in terms of - # u = ln(scale_to/scale_from) + beta_qcd_vec = [beta_qcd((2, 0), nf)] beta_qcd_mix = 0 + beta_qed_mix = 0 # NLO if self.order[0] >= 2: beta_qcd_vec.append(beta_qcd((3, 0), nf)) - beta_qed_mix = beta_qed((1, 2), nf) + # beta_qed_mix = beta_qed((1, 2), nf) # NNLO if self.order[0] >= 3: beta_qcd_vec.append(beta_qcd((4, 0), nf)) # N3LO if self.order[0] >= 4: beta_qcd_vec.append(beta_qcd((5, 0), nf)) - beta_qed_vec = [beta_qed((0, 2), nf)] - beta_qed_mix = 0 - if self.order[1] >= 2: + if self.order[1] == 0: + b_qcd_vec = [ + beta_qcd_vec[i] / beta_qcd_vec[0] for i in range(self.order[0]) + ] + rge_qcd = self.unidimensional_exact( + beta_qcd_vec[0], + b_qcd_vec, + u, + a_ref[0], + method="Radau", + rtol=1e-6, + ) + # for order = (qcd, 0) with qcd > 1 we return the exact solution for the QCD RGE + # while aem is constant + return np.array([rge_qcd, a_ref[1]]) + if self.order[1] >= 1: + beta_qed_vec = [beta_qed((0, 2), nf)] beta_qcd_mix = beta_qcd((2, 1), nf) + beta_qed_mix = beta_qed((1, 2), nf) # order[0] is always at least 1 + if self.order[1] >= 2: beta_qed_vec.append(beta_qed((0, 3), nf)) # integration kernel def rge(_t, a, beta_qcd_vec, beta_qed_vec): @@ -584,6 +651,58 @@ def rge(_t, a, beta_qcd_vec, beta_qed_vec): ) return np.array([res.y[0][-1], res.y[1][-1]]) + def compute_exact_fixed_alphaem(self, a_ref, nf, scale_from, scale_to): + """Compute couplings via |RGE|. + + Parameters + ---------- + as_ref : numpy.ndarray + reference alpha_s and alpha + nf : int + value of nf for computing alpha_i + scale_from : float + reference scale + scale_to : float + target scale + + Returns + ------- + numpy.ndarray + couplings at target scale :math:`a(Q^2)` + """ + u = np.log(scale_to / scale_from) + + if self.order in [(1, 0), (1, 1)]: + return couplings_expanded_fixed_alphaem( + self.order, a_ref, nf, scale_from, float(scale_to) + ) + + beta0 = beta_qcd((2, 0), nf) + beta_vec = [] + # NLO + if self.order[0] >= 2: + beta_vec.append(beta_qcd((3, 0), nf)) + # beta_qed_mix = beta_qed((1, 2), nf) + # NNLO + if self.order[0] >= 3: + beta_vec.append(beta_qcd((4, 0), nf)) + # N3LO + if self.order[0] >= 4: + beta_vec.append(beta_qcd((5, 0), nf)) + if self.order[1] >= 1: + beta0 += a_ref[1] * beta_qcd((2, 1), nf) + b_vec = [i / beta0 for i in beta_vec] + b_vec.insert(0, 1.0) + rge_qcd = self.unidimensional_exact( + beta0, + b_vec, + u, + a_ref[0], + method="Radau", + rtol=1e-6, + ) + return np.array([rge_qcd, a_ref[1]]) + def compute(self, a_ref, nf, scale_from, scale_to): """Compute actual couplings. @@ -612,13 +731,23 @@ def compute(self, a_ref, nf, scale_from, scale_to): except KeyError: # at the moment everything is expanded - and type has been checked in the constructor if self.method == "exact": - a_new = self.compute_exact( - a_ref.astype(float), nf, scale_from, scale_to - ) + if self.running_alphaem: + a_new = self.compute_exact_running_alphaem( + a_ref.astype(float), nf, scale_from, scale_to + ) + else: + a_new = self.compute_exact_fixed_alphaem( + a_ref.astype(float), nf, scale_from, scale_to + ) else: - a_new = couplings_expanded( - self.order, a_ref.astype(float), nf, scale_from, float(scale_to) - ) + if self.running_alphaem: + a_new = couplings_expanded_running_alphaem( + self.order, a_ref.astype(float), nf, scale_from, float(scale_to) + ) + else: + a_new = couplings_expanded_fixed_alphaem( + self.order, a_ref.astype(float), nf, scale_from, float(scale_to) + ) self.cache[key] = a_new.copy() return a_new diff --git a/tests/eko/test_couplings.py b/tests/eko/test_couplings.py index b6ee611f4..1c2acf9c2 100644 --- a/tests/eko/test_couplings.py +++ b/tests/eko/test_couplings.py @@ -56,7 +56,7 @@ def test_init(self): assert sc.a_ref[1] == couplings_ref[1] / 4.0 / np.pi # from theory dict for ModEv in ["EXP", "EXA"]: - for PTOs in range(3 + 1): + for PTOs in range(1, 3 + 1): for PTOem in range(2 + 1): setup = dict( alphas=alphas_ref, @@ -162,26 +162,28 @@ def test_ref(self): alphaem_ref = 0.00781 scale_ref = 91.0**2 for thresh_setup in thresh_setups: - for order_s in [0, 1, 2, 3, 4]: + for order_s in [1, 2, 3, 4]: for order_em in [0, 1, 2]: for method in ["exact", "expanded"]: - # if order_em == 1 and method == "expanded" and order_s != 0: - # continue - # create - sc = Couplings( - np.array([alphas_ref, alphaem_ref]), - scale_ref, - thresh_setup, - (1.0, 1.0, 1.0), - (order_s, order_em), - method, - ) - np.testing.assert_approx_equal( - sc.a(scale_ref)[0], alphas_ref / 4.0 / np.pi - ) - np.testing.assert_approx_equal( - sc.a(scale_ref)[1], alphaem_ref / 4.0 / np.pi - ) + for running_alphaem in [True, False]: + # if order_em == 1 and method == "expanded" and order_s != 0: + # continue + # create + sc = Couplings( + np.array([alphas_ref, alphaem_ref]), + scale_ref, + thresh_setup, + (1.0, 1.0, 1.0), + (order_s, order_em), + method, + running_alphaem=running_alphaem, + ) + np.testing.assert_approx_equal( + sc.a(scale_ref)[0], alphas_ref / 4.0 / np.pi + ) + np.testing.assert_approx_equal( + sc.a(scale_ref)[1], alphaem_ref / 4.0 / np.pi + ) def test_ref_copy_e841b0dfdee2f31d9ccc1ecee4d9d1a6f6624313(self): """Reference settings are an array with QED, but were not copied.""" @@ -256,32 +258,35 @@ def test_exact_LO_QED(self): alphas_ref = 0.118 alphaem_ref = 0.00781 scale_ref = 91.0**2 - for PTOs in range(1, 4 + 1): + for PTOs in range(1, 2 + 1): for thresh_setup in thresh_setups: - # in LO expanded = exact - sc_expanded = Couplings( - np.array([alphas_ref, alphaem_ref]), - scale_ref, - thresh_setup, - (1.0, 1.0, 1.0), - (PTOs, 1), - "expanded", - ) - sc_exact = Couplings( - np.array([alphas_ref, alphaem_ref]), - scale_ref, - thresh_setup, - (1.0, 1.0, 1.0), - (PTOs, 1), - "exact", - ) - for q2 in [1e2, 1e3, 1e4]: - np.testing.assert_allclose( - sc_expanded.a(q2)[0], sc_exact.a(q2)[0], atol=1e-4 + for running_alphaem in [True, False]: + # in LO expanded = exact + sc_expanded = Couplings( + np.array([alphas_ref, alphaem_ref]), + scale_ref, + thresh_setup, + (1.0, 1.0, 1.0), + (PTOs, 1), + "expanded", + running_alphaem=running_alphaem, ) - np.testing.assert_allclose( - sc_expanded.a(q2)[1], sc_exact.a(q2)[1], atol=5e-4 + sc_exact = Couplings( + np.array([alphas_ref, alphaem_ref]), + scale_ref, + thresh_setup, + (1.0, 1.0, 1.0), + (PTOs, 1), + "exact", + running_alphaem=running_alphaem, ) + for q2 in [1e2, 1e3, 1e4]: + np.testing.assert_allclose( + sc_expanded.a(q2)[0], sc_exact.a(q2)[0], atol=1e-4 + ) + np.testing.assert_allclose( + sc_expanded.a(q2)[1], sc_exact.a(q2)[1], atol=5e-4 + ) def test_exact_NLO_QED(self): # prepare @@ -293,31 +298,35 @@ def test_exact_NLO_QED(self): alphas_ref = 0.118 alphaem_ref = 0.00781 scale_ref = 91.0**2 - for thresh_setup in thresh_setups: - # in LO expanded = exact - sc_expanded = Couplings( - np.array([alphas_ref, alphaem_ref]), - scale_ref, - thresh_setup, - (1.0, 1.0, 1.0), - (0, 2), - "expanded", - ) - sc_exact = Couplings( - np.array([alphas_ref, alphaem_ref]), - scale_ref, - thresh_setup, - (1.0, 1.0, 1.0), - (0, 2), - "exact", - ) - for q2 in [1, 1e1, 1e2, 1e3, 1e4]: - np.testing.assert_allclose( - sc_expanded.a(q2)[0], sc_exact.a(q2)[0], rtol=5e-4 - ) - np.testing.assert_allclose( - sc_expanded.a(q2)[1], sc_exact.a(q2)[1], rtol=5e-4 - ) + for PTOs in range(1, 4 + 1): + for thresh_setup in thresh_setups: + for running_alphaem in [True, False]: + # in LO expanded = exact + sc_expanded = Couplings( + np.array([alphas_ref, alphaem_ref]), + scale_ref, + thresh_setup, + (1.0, 1.0, 1.0), + (PTOs, 2), + "expanded", + running_alphaem=running_alphaem, + ) + sc_exact = Couplings( + np.array([alphas_ref, alphaem_ref]), + scale_ref, + thresh_setup, + (1.0, 1.0, 1.0), + (PTOs, 2), + "exact", + running_alphaem=running_alphaem, + ) + for q2 in [1e2, 1e3, 1e4]: + np.testing.assert_allclose( + sc_expanded.a(q2)[0], sc_exact.a(q2)[0], atol=5e-4 + ) + np.testing.assert_allclose( + sc_expanded.a(q2)[1], sc_exact.a(q2)[1], atol=5e-4 + ) def test_exact_NLO_mix(self): # prepare @@ -330,30 +339,33 @@ def test_exact_NLO_mix(self): alphaem_ref = 0.00781 scale_ref = 91.0**2 for thresh_setup in thresh_setups: - # in LO expanded = exact - sc_expanded = Couplings( - np.array([alphas_ref, alphaem_ref]), - scale_ref, - thresh_setup, - (1.0, 1.0, 1.0), - (2, 2), - "expanded", - ) - sc_exact = Couplings( - np.array([alphas_ref, alphaem_ref]), - scale_ref, - thresh_setup, - (1.0, 1.0, 1.0), - (2, 2), - "exact", - ) - for q2 in [1e2, 1e3, 1e4]: - np.testing.assert_allclose( - sc_expanded.a(q2)[0], sc_exact.a(q2)[0], atol=5e-4 + for running_alphaem in [True, False]: + # in LO expanded = exact + sc_expanded = Couplings( + np.array([alphas_ref, alphaem_ref]), + scale_ref, + thresh_setup, + (1.0, 1.0, 1.0), + (2, 2), + "expanded", + running_alphaem=running_alphaem, ) - np.testing.assert_allclose( - sc_expanded.a(q2)[1], sc_exact.a(q2)[1], atol=5e-4 + sc_exact = Couplings( + np.array([alphas_ref, alphaem_ref]), + scale_ref, + thresh_setup, + (1.0, 1.0, 1.0), + (2, 2), + "exact", + running_alphaem=running_alphaem, ) + for q2 in [1e2, 1e3, 1e4]: + np.testing.assert_allclose( + sc_expanded.a(q2)[0], sc_exact.a(q2)[0], atol=5e-4 + ) + np.testing.assert_allclose( + sc_expanded.a(q2)[1], sc_exact.a(q2)[1], atol=5e-4 + ) def test_exact_N2LO_mix(self): # prepare @@ -366,30 +378,33 @@ def test_exact_N2LO_mix(self): alphaem_ref = 0.00781 scale_ref = 91.0**2 for thresh_setup in thresh_setups: - # in LO expanded = exact - sc_expanded = Couplings( - np.array([alphas_ref, alphaem_ref]), - scale_ref, - thresh_setup, - (1.0, 1.0, 1.0), - (3, 2), - "expanded", - ) - sc_exact = Couplings( - np.array([alphas_ref, alphaem_ref]), - scale_ref, - thresh_setup, - (1.0, 1.0, 1.0), - (3, 2), - "exact", - ) - for q2 in [1e1, 1e2, 1e3, 1e4]: - np.testing.assert_allclose( - sc_expanded.a(q2)[0], sc_exact.a(q2)[0], atol=5e-4 + for running_alphaem in [True, False]: + # in LO expanded = exact + sc_expanded = Couplings( + np.array([alphas_ref, alphaem_ref]), + scale_ref, + thresh_setup, + (1.0, 1.0, 1.0), + (3, 2), + "expanded", + running_alphaem=running_alphaem, ) - np.testing.assert_allclose( - sc_expanded.a(q2)[1], sc_exact.a(q2)[1], atol=5e-4 + sc_exact = Couplings( + np.array([alphas_ref, alphaem_ref]), + scale_ref, + thresh_setup, + (1.0, 1.0, 1.0), + (3, 2), + "exact", + running_alphaem=running_alphaem, ) + for q2 in [1e1, 1e2, 1e3, 1e4]: + np.testing.assert_allclose( + sc_expanded.a(q2)[0], sc_exact.a(q2)[0], atol=5e-4 + ) + np.testing.assert_allclose( + sc_expanded.a(q2)[1], sc_exact.a(q2)[1], atol=5e-4 + ) def test_exact_N3LO_mix(self): # prepare @@ -402,30 +417,33 @@ def test_exact_N3LO_mix(self): alphaem_ref = 0.00781 scale_ref = 91.0**2 for thresh_setup in thresh_setups: - # in LO expanded = exact - sc_expanded = Couplings( - np.array([alphas_ref, alphaem_ref]), - scale_ref, - thresh_setup, - (1.0, 1.0, 1.0), - (4, 2), - "expanded", - ) - sc_exact = Couplings( - np.array([alphas_ref, alphaem_ref]), - scale_ref, - thresh_setup, - (1.0, 1.0, 1.0), - (4, 2), - "exact", - ) - for q2 in [1e1, 1e2, 1e3, 1e4]: - np.testing.assert_allclose( - sc_expanded.a(q2)[0], sc_exact.a(q2)[0], atol=5e-3 + for running_alphaem in [True, False]: + # in LO expanded = exact + sc_expanded = Couplings( + np.array([alphas_ref, alphaem_ref]), + scale_ref, + thresh_setup, + (1.0, 1.0, 1.0), + (4, 2), + "expanded", + running_alphaem=running_alphaem, ) - np.testing.assert_allclose( - sc_expanded.a(q2)[1], sc_exact.a(q2)[1], atol=5e-4 + sc_exact = Couplings( + np.array([alphas_ref, alphaem_ref]), + scale_ref, + thresh_setup, + (1.0, 1.0, 1.0), + (4, 2), + "exact", + running_alphaem=running_alphaem, ) + for q2 in [1e1, 1e2, 1e3, 1e4]: + np.testing.assert_allclose( + sc_expanded.a(q2)[0], sc_exact.a(q2)[0], atol=5e-3 + ) + np.testing.assert_allclose( + sc_expanded.a(q2)[1], sc_exact.a(q2)[1], atol=5e-4 + ) def benchmark_expanded_n3lo(self): """test N3LO - NNLO expansion with some reference value from Mathematica""" From 49351dd7dbf626cec6a966fcf1110b5006474386 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 23 Sep 2022 12:45:49 +0200 Subject: [PATCH 154/312] Test exact running alpha at ord (1,0), (2,0) --- tests/eko/test_couplings.py | 94 ++++++++++++++++++++++++++++--------- 1 file changed, 72 insertions(+), 22 deletions(-) diff --git a/tests/eko/test_couplings.py b/tests/eko/test_couplings.py index 1c2acf9c2..bcf7c6b38 100644 --- a/tests/eko/test_couplings.py +++ b/tests/eko/test_couplings.py @@ -92,6 +92,14 @@ def test_init(self): threshold_holder.area_walls[1:-1], (1.0, 1.0, 1.0), ) + with pytest.raises(ValueError): + Couplings( + [couplings_ref[0], couplings_ref[1]], + scale_ref, + threshold_holder.area_walls[1:-1], + (1.0, 1.0, 1.0), + (0, 2), + ) with pytest.raises(ValueError): Couplings( [couplings_ref[0], 0], @@ -223,30 +231,72 @@ def test_exact_LO(self): alphaem_ref = 0.00781 scale_ref = 91.0**2 for thresh_setup in thresh_setups: - # in LO expanded = exact - sc_expanded = Couplings( - np.array([alphas_ref, alphaem_ref]), - scale_ref, - thresh_setup, - (1.0, 1.0, 1.0), - (1, 0), - "expanded", - ) - sc_exact = Couplings( - np.array([alphas_ref, alphaem_ref]), - scale_ref, - thresh_setup, - (1.0, 1.0, 1.0), - (1, 0), - "exact", - ) - for q2 in [1, 1e1, 1e2, 1e3, 1e4]: - np.testing.assert_allclose( - sc_expanded.a(q2)[0], sc_exact.a(q2)[0], rtol=5e-4 + for running_alphaem in [True, False]: + # in LO expanded = exact + sc_expanded = Couplings( + np.array([alphas_ref, alphaem_ref]), + scale_ref, + thresh_setup, + (1.0, 1.0, 1.0), + (1, 0), + "expanded", + running_alphaem=running_alphaem, + ) + sc_exact = Couplings( + np.array([alphas_ref, alphaem_ref]), + scale_ref, + thresh_setup, + (1.0, 1.0, 1.0), + (1, 0), + "exact", + running_alphaem=running_alphaem, + ) + for q2 in [1, 1e1, 1e2, 1e3, 1e4]: + np.testing.assert_allclose( + sc_expanded.a(q2)[0], sc_exact.a(q2)[0], rtol=5e-4 + ) + np.testing.assert_allclose( + sc_expanded.a(q2)[1], sc_exact.a(q2)[1], rtol=5e-4 + ) + + def test_exact_NLO(self): + # prepare + thresh_setups = [ + (np.inf, np.inf, np.inf), + (0, np.inf, np.inf), + (2, 4, 175), + ] + alphas_ref = 0.118 + alphaem_ref = 0.00781 + scale_ref = 91.0**2 + for thresh_setup in thresh_setups: + for running_alphaem in [True, False]: + # in LO expanded = exact + sc_expanded = Couplings( + np.array([alphas_ref, alphaem_ref]), + scale_ref, + thresh_setup, + (1.0, 1.0, 1.0), + (2, 0), + "expanded", + running_alphaem=running_alphaem, ) - np.testing.assert_allclose( - sc_expanded.a(q2)[1], sc_exact.a(q2)[1], rtol=5e-4 + sc_exact = Couplings( + np.array([alphas_ref, alphaem_ref]), + scale_ref, + thresh_setup, + (1.0, 1.0, 1.0), + (2, 0), + "exact", + running_alphaem=running_alphaem, ) + for q2 in [1e2, 1e3, 1e4]: + np.testing.assert_allclose( + sc_expanded.a(q2)[0], sc_exact.a(q2)[0], atol=5e-4 + ) + np.testing.assert_allclose( + sc_expanded.a(q2)[1], sc_exact.a(q2)[1], atol=5e-4 + ) def test_exact_LO_QED(self): # prepare From 681b49e81ed7294e6f3d2223fd06b346bf181852 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 23 Sep 2022 16:12:36 +0200 Subject: [PATCH 155/312] Add comment about running_alphaem --- src/eko/couplings.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/eko/couplings.py b/src/eko/couplings.py index 73503831e..f7c19410e 100644 --- a/src/eko/couplings.py +++ b/src/eko/couplings.py @@ -448,6 +448,8 @@ def __init__( raise ValueError(f"Unknown method {method}") self.method = method self.running_alphaem = running_alphaem + # running_alphaem is put for a future implementation of the DGLAP solution + # with running alpha. For the moment in must remain set to False # create new threshold object self.a_ref = couplings_ref.copy() / 4.0 / np.pi # convert to a_s and a_em From 1c13476cae66e3d423102b131e5a02e29435194a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 23 Sep 2022 16:25:29 +0200 Subject: [PATCH 156/312] Add test_running_alpha --- src/eko/couplings.py | 19 +++++++++---- tests/eko/test_couplings.py | 57 +++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 6 deletions(-) diff --git a/src/eko/couplings.py b/src/eko/couplings.py index f7c19410e..630a1f1c3 100644 --- a/src/eko/couplings.py +++ b/src/eko/couplings.py @@ -206,23 +206,29 @@ def expanded_qcd(ref, order, nf, lmu): strong coupling at target scale :math:`a_s(\mu_R^2)` """ res_as = ref - if order >= 1: + if order == 1: beta_qcd0 = beta_qcd((2, 0), nf) # QCD LO as_LO = exact_lo(ref, beta_qcd0, lmu) res_as = as_LO # NLO - if order >= 2: + if order == 2: + beta_qcd0 = beta_qcd((2, 0), nf) b_qcd1 = b_qcd((3, 0), nf) as_NLO = expanded_nlo(ref, beta_qcd0, b_qcd1, lmu) res_as = as_NLO # NNLO - if order >= 3: + if order == 3: + beta_qcd0 = beta_qcd((2, 0), nf) + b_qcd1 = b_qcd((3, 0), nf) b_qcd2 = b_qcd((4, 0), nf) as_NNLO = expanded_nnlo(ref, beta_qcd0, b_qcd1, b_qcd2, lmu) res_as = as_NNLO # N3LO - if order >= 4: + if order == 4: + beta_qcd0 = beta_qcd((2, 0), nf) + b_qcd1 = b_qcd((3, 0), nf) + b_qcd2 = b_qcd((4, 0), nf) b_qcd3 = b_qcd((5, 0), nf) as_N3LO = expanded_n3lo( ref, @@ -257,13 +263,14 @@ def expanded_qed(ref, order, nf, lmu): QED coupling at target scale :math:`a_em(\mu_R^2)` """ res_aem = ref - if order >= 1: + if order == 1: beta_qed0 = beta_qed((0, 2), nf) # QED LO aem_LO = exact_lo(ref, beta_qed0, lmu) res_aem = aem_LO # NLO - if order >= 2: + if order == 2: + beta_qed0 = beta_qed((0, 2), nf) b_qed1 = b_qed((0, 3), nf) aem_NLO = expanded_nlo(ref, beta_qed0, b_qed1, lmu) res_aem = aem_NLO diff --git a/tests/eko/test_couplings.py b/tests/eko/test_couplings.py index bcf7c6b38..0f681104d 100644 --- a/tests/eko/test_couplings.py +++ b/tests/eko/test_couplings.py @@ -527,3 +527,60 @@ def benchmark_expanded_n3lo(self): np.testing.assert_allclose( mathematica_val, as_N3LO.a(Q2)[0] - as_NNLO.a(Q2)[0], rtol=3e-6 ) + + def test_running_alpha(self): + # prepare + thresh_setups = [ + (np.inf, np.inf, np.inf), + (0, np.inf, np.inf), + (2, 4, 175), + ] + alphas_ref = 0.118 + alphaem_ref = 0.00781 + scale_ref = 91.0**2 + for thresh_setup in thresh_setups: + for qcd in range(1, 4 + 1): + sc_expanded_running = Couplings( + np.array([alphas_ref, alphaem_ref]), + scale_ref, + thresh_setup, + (1.0, 1.0, 1.0), + (qcd, 0), + "expanded", + running_alphaem=True, + ) + sc_expanded_fixed = Couplings( + np.array([alphas_ref, alphaem_ref]), + scale_ref, + thresh_setup, + (1.0, 1.0, 1.0), + (qcd, 0), + "expanded", + running_alphaem=False, + ) + sc_exact_running = Couplings( + np.array([alphas_ref, alphaem_ref]), + scale_ref, + thresh_setup, + (1.0, 1.0, 1.0), + (qcd, 0), + "exact", + running_alphaem=True, + ) + sc_exact_fixed = Couplings( + np.array([alphas_ref, alphaem_ref]), + scale_ref, + thresh_setup, + (1.0, 1.0, 1.0), + (qcd, 0), + "exact", + running_alphaem=False, + ) + for q2 in [1e1, 1e2, 1e3, 1e4]: + # for pure qcd running or fixed alpha must be equal + np.testing.assert_allclose( + sc_expanded_running.a(q2), sc_expanded_fixed.a(q2), rtol=1e-10 + ) + np.testing.assert_allclose( + sc_exact_running.a(q2), sc_exact_fixed.a(q2), rtol=1e-10 + ) From 1ef58fecbf1709f17006b8687c4600bee111d04f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 23 Sep 2022 16:37:26 +0200 Subject: [PATCH 157/312] Modify compute_exact_fixed_alphaem --- src/eko/couplings.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/eko/couplings.py b/src/eko/couplings.py index 630a1f1c3..290d0f8e1 100644 --- a/src/eko/couplings.py +++ b/src/eko/couplings.py @@ -351,20 +351,24 @@ def couplings_expanded_fixed_alphaem(order, couplings_ref, nf, scale_from, scale if order[1] >= 1: beta_qcd0 += aem * beta_qcd((2, 1), nf) # QCD LO - as_LO = exact_lo(couplings_ref[0], beta_qcd0, lmu) - res_as = as_LO + if order[0] == 1: + as_LO = exact_lo(couplings_ref[0], beta_qcd0, lmu) + res_as = as_LO # NLO - if order[0] >= 2: + if order[0] == 2: b_qcd1 = beta_qcd((3, 0), nf) / beta_qcd0 as_NLO = expanded_nlo(couplings_ref[0], beta_qcd0, b_qcd1, lmu) res_as = as_NLO # NNLO - if order[0] >= 3: + if order[0] == 3: + b_qcd1 = beta_qcd((3, 0), nf) / beta_qcd0 b_qcd2 = beta_qcd((4, 0), nf) / beta_qcd0 as_NNLO = expanded_nnlo(couplings_ref[0], beta_qcd0, b_qcd1, b_qcd2, lmu) res_as = as_NNLO # N3LO - if order[0] >= 4: + if order[0] == 4: + b_qcd1 = beta_qcd((3, 0), nf) / beta_qcd0 + b_qcd2 = beta_qcd((4, 0), nf) / beta_qcd0 b_qcd3 = beta_qcd((5, 0), nf) / beta_qcd0 as_N3LO = expanded_n3lo( couplings_ref[0], @@ -608,7 +612,6 @@ def compute_exact_running_alphaem(self, a_ref, nf, scale_from, scale_to): # NLO if self.order[0] >= 2: beta_qcd_vec.append(beta_qcd((3, 0), nf)) - # beta_qed_mix = beta_qed((1, 2), nf) # NNLO if self.order[0] >= 3: beta_qcd_vec.append(beta_qcd((4, 0), nf)) @@ -686,8 +689,7 @@ def compute_exact_fixed_alphaem(self, a_ref, nf, scale_from, scale_to): self.order, a_ref, nf, scale_from, float(scale_to) ) - beta0 = beta_qcd((2, 0), nf) - beta_vec = [] + beta_vec = [beta_qcd((2, 0), nf)] # NLO if self.order[0] >= 2: beta_vec.append(beta_qcd((3, 0), nf)) @@ -699,11 +701,10 @@ def compute_exact_fixed_alphaem(self, a_ref, nf, scale_from, scale_to): if self.order[0] >= 4: beta_vec.append(beta_qcd((5, 0), nf)) if self.order[1] >= 1: - beta0 += a_ref[1] * beta_qcd((2, 1), nf) - b_vec = [i / beta0 for i in beta_vec] - b_vec.insert(0, 1.0) + beta_vec[0] += a_ref[1] * beta_qcd((2, 1), nf) + b_vec = [i / beta_vec[0] for i in beta_vec] rge_qcd = self.unidimensional_exact( - beta0, + beta_vec[0], b_vec, u, a_ref[0], From fd4aed63163a15577e4c9896a0b6efc71f03298f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 26 Sep 2022 09:48:16 +0200 Subject: [PATCH 158/312] Remove TODO --- src/eko/evolution_operator/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 5823eb412..be7e1fbad 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -139,7 +139,6 @@ class QuadKerBase: def __init__(self, u, is_log, logx, mode0): self.is_singlet = mode0 in [100, 21, 90] - # TODO : check 90 in is_QEDsinglet and 91 in is_QEDvalence self.is_QEDsinglet = mode0 in [21, 22, 100, 101, 90] self.is_QEDvalence = mode0 in [10200, 10204] self.is_log = is_log From f3ba1a21e39eeec2b056e29c0cfb525bcc0504e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 26 Sep 2022 11:26:54 +0200 Subject: [PATCH 159/312] Compute beta coeff before if statements --- src/eko/couplings.py | 84 ++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 54 deletions(-) diff --git a/src/eko/couplings.py b/src/eko/couplings.py index 290d0f8e1..c659baf30 100644 --- a/src/eko/couplings.py +++ b/src/eko/couplings.py @@ -206,39 +206,25 @@ def expanded_qcd(ref, order, nf, lmu): strong coupling at target scale :math:`a_s(\mu_R^2)` """ res_as = ref + beta_vec = [beta_qcd((2, 0), nf)] + for i in range(2, order + 1): + beta_vec.append(b_qcd((i + 1, 0), nf)) + # LO if order == 1: - beta_qcd0 = beta_qcd((2, 0), nf) - # QCD LO - as_LO = exact_lo(ref, beta_qcd0, lmu) - res_as = as_LO + res_as = exact_lo(ref, *beta_vec, lmu) # NLO if order == 2: - beta_qcd0 = beta_qcd((2, 0), nf) - b_qcd1 = b_qcd((3, 0), nf) - as_NLO = expanded_nlo(ref, beta_qcd0, b_qcd1, lmu) - res_as = as_NLO + res_as = expanded_nlo(ref, *beta_vec, lmu) # NNLO if order == 3: - beta_qcd0 = beta_qcd((2, 0), nf) - b_qcd1 = b_qcd((3, 0), nf) - b_qcd2 = b_qcd((4, 0), nf) - as_NNLO = expanded_nnlo(ref, beta_qcd0, b_qcd1, b_qcd2, lmu) - res_as = as_NNLO + res_as = expanded_nnlo(ref, *beta_vec, lmu) # N3LO if order == 4: - beta_qcd0 = beta_qcd((2, 0), nf) - b_qcd1 = b_qcd((3, 0), nf) - b_qcd2 = b_qcd((4, 0), nf) - b_qcd3 = b_qcd((5, 0), nf) - as_N3LO = expanded_n3lo( + res_as = expanded_n3lo( ref, - beta_qcd0, - b_qcd1, - b_qcd2, - b_qcd3, + *beta_vec, lmu, ) - res_as = as_N3LO return res_as @@ -263,23 +249,23 @@ def expanded_qed(ref, order, nf, lmu): QED coupling at target scale :math:`a_em(\mu_R^2)` """ res_aem = ref + beta_vec = [beta_qed((0, 2), nf)] + for i in range(2, order + 1): + beta_vec.append(b_qed((0, i + 1), nf)) + # LO if order == 1: - beta_qed0 = beta_qed((0, 2), nf) - # QED LO - aem_LO = exact_lo(ref, beta_qed0, lmu) - res_aem = aem_LO + res_aem = exact_lo(ref, *beta_vec, lmu) # NLO if order == 2: - beta_qed0 = beta_qed((0, 2), nf) - b_qed1 = b_qed((0, 3), nf) - aem_NLO = expanded_nlo(ref, beta_qed0, b_qed1, lmu) - res_aem = aem_NLO + res_aem = expanded_nlo(ref, *beta_vec, lmu) return res_aem @nb.njit(cache=True) def couplings_expanded_running_alphaem(order, couplings_ref, nf, scale_from, scale_to): - r"""Compute expanded expression. + r"""Compute coupled expanded expression of the couplings for running alphaem. + + Implement Eqs. (17-18) from :cite:`Surguladze:1996hx` Parameters ---------- @@ -323,7 +309,7 @@ def couplings_expanded_running_alphaem(order, couplings_ref, nf, scale_from, sca @nb.njit(cache=True) def couplings_expanded_fixed_alphaem(order, couplings_ref, nf, scale_from, scale_to): - r"""Compute expanded expression. + r"""Compute coupled expanded expression of the couplings for fixed alphaem. Parameters ---------- @@ -350,35 +336,25 @@ def couplings_expanded_fixed_alphaem(order, couplings_ref, nf, scale_from, scale beta_qcd0 = beta_qcd((2, 0), nf) if order[1] >= 1: beta_qcd0 += aem * beta_qcd((2, 1), nf) - # QCD LO + beta_vec = [beta_qcd0] + for i in range(2, order[0] + 1): + beta_vec.append(beta_qcd((i + 1, 0), nf) / beta_qcd0) + # LO if order[0] == 1: - as_LO = exact_lo(couplings_ref[0], beta_qcd0, lmu) - res_as = as_LO + res_as = exact_lo(couplings_ref[0], *beta_vec, lmu) # NLO if order[0] == 2: - b_qcd1 = beta_qcd((3, 0), nf) / beta_qcd0 - as_NLO = expanded_nlo(couplings_ref[0], beta_qcd0, b_qcd1, lmu) - res_as = as_NLO + res_as = expanded_nlo(couplings_ref[0], *beta_vec, lmu) # NNLO if order[0] == 3: - b_qcd1 = beta_qcd((3, 0), nf) / beta_qcd0 - b_qcd2 = beta_qcd((4, 0), nf) / beta_qcd0 - as_NNLO = expanded_nnlo(couplings_ref[0], beta_qcd0, b_qcd1, b_qcd2, lmu) - res_as = as_NNLO + res_as = expanded_nnlo(couplings_ref[0], *beta_vec, lmu) # N3LO if order[0] == 4: - b_qcd1 = beta_qcd((3, 0), nf) / beta_qcd0 - b_qcd2 = beta_qcd((4, 0), nf) / beta_qcd0 - b_qcd3 = beta_qcd((5, 0), nf) / beta_qcd0 - as_N3LO = expanded_n3lo( + res_as = expanded_n3lo( couplings_ref[0], - beta_qcd0, - b_qcd1, - b_qcd2, - b_qcd3, + *beta_vec, lmu, ) - res_as = as_N3LO return np.array([res_as, aem]) @@ -580,7 +556,7 @@ def rge(_t, a, b_vec): return res.y[0][-1] def compute_exact_running_alphaem(self, a_ref, nf, scale_from, scale_to): - """Compute couplings via |RGE|. + """Compute couplings via |RGE| with running alphaem. Parameters ---------- @@ -664,7 +640,7 @@ def rge(_t, a, beta_qcd_vec, beta_qed_vec): return np.array([res.y[0][-1], res.y[1][-1]]) def compute_exact_fixed_alphaem(self, a_ref, nf, scale_from, scale_to): - """Compute couplings via |RGE|. + """Compute couplings via |RGE| with fixed alphaem. Parameters ---------- From 18c33f784c103e3eb5a37870b558a45ca7c4f695 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 26 Sep 2022 11:49:46 +0200 Subject: [PATCH 160/312] Change calling of beta_vec --- src/eko/couplings.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/eko/couplings.py b/src/eko/couplings.py index c659baf30..f86d9cae1 100644 --- a/src/eko/couplings.py +++ b/src/eko/couplings.py @@ -211,18 +211,21 @@ def expanded_qcd(ref, order, nf, lmu): beta_vec.append(b_qcd((i + 1, 0), nf)) # LO if order == 1: - res_as = exact_lo(ref, *beta_vec, lmu) + res_as = exact_lo(ref, beta_vec[0], lmu) # NLO if order == 2: - res_as = expanded_nlo(ref, *beta_vec, lmu) + res_as = expanded_nlo(ref, beta_vec[0], beta_vec[1], lmu) # NNLO if order == 3: - res_as = expanded_nnlo(ref, *beta_vec, lmu) + res_as = expanded_nnlo(ref, beta_vec[0], beta_vec[1], beta_vec[2], lmu) # N3LO if order == 4: res_as = expanded_n3lo( ref, - *beta_vec, + beta_vec[0], + beta_vec[1], + beta_vec[2], + beta_vec[3], lmu, ) return res_as @@ -254,10 +257,10 @@ def expanded_qed(ref, order, nf, lmu): beta_vec.append(b_qed((0, i + 1), nf)) # LO if order == 1: - res_aem = exact_lo(ref, *beta_vec, lmu) + res_aem = exact_lo(ref, beta_vec[0], lmu) # NLO if order == 2: - res_aem = expanded_nlo(ref, *beta_vec, lmu) + res_aem = expanded_nlo(ref, beta_vec[0], beta_vec[1], lmu) return res_aem @@ -341,18 +344,23 @@ def couplings_expanded_fixed_alphaem(order, couplings_ref, nf, scale_from, scale beta_vec.append(beta_qcd((i + 1, 0), nf) / beta_qcd0) # LO if order[0] == 1: - res_as = exact_lo(couplings_ref[0], *beta_vec, lmu) + res_as = exact_lo(couplings_ref[0], beta_vec[0], lmu) # NLO if order[0] == 2: - res_as = expanded_nlo(couplings_ref[0], *beta_vec, lmu) + res_as = expanded_nlo(couplings_ref[0], beta_vec[0], beta_vec[1], lmu) # NNLO if order[0] == 3: - res_as = expanded_nnlo(couplings_ref[0], *beta_vec, lmu) + res_as = expanded_nnlo( + couplings_ref[0], beta_vec[0], beta_vec[1], beta_vec[2], lmu + ) # N3LO if order[0] == 4: res_as = expanded_n3lo( couplings_ref[0], - *beta_vec, + beta_vec[0], + beta_vec[1], + beta_vec[2], + beta_vec[3], lmu, ) return np.array([res_as, aem]) From c1e9c8de9c4f9f2790a85c373d7f064c4cf7fb04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 26 Sep 2022 11:58:28 +0200 Subject: [PATCH 161/312] Fix documentation in kernels/QEDsinglet and valence --- src/eko/kernels/QEDsinglet.py | 28 ++---------------------- src/eko/kernels/QEDvalence.py | 40 +++++++++-------------------------- 2 files changed, 12 insertions(+), 56 deletions(-) diff --git a/src/eko/kernels/QEDsinglet.py b/src/eko/kernels/QEDsinglet.py index e3f73978d..7f5f3fc5e 100644 --- a/src/eko/kernels/QEDsinglet.py +++ b/src/eko/kernels/QEDsinglet.py @@ -64,8 +64,6 @@ def dispatcher( # pylint: disable=too-many-return-statements """ Determine used kernel and call it. - In LO we always use the exact solution. - Parameters ---------- order : tuple(int,int) @@ -78,6 +76,8 @@ def dispatcher( # pylint: disable=too-many-return-statements target coupling value a0 : float initial coupling value + aem : float + electromagnetic coupling value nf : int number of active flavors ev_op_iterations : int @@ -90,30 +90,6 @@ def dispatcher( # pylint: disable=too-many-return-statements e_s : numpy.ndarray singlet EKO """ - # use always exact in LO - # if order == (1,0): - # return lo_exact(gamma_singlet, a1, a0, nf) - if method in ["iterate-exact", "iterate-expanded"]: return eko_iterate(gamma_singlet, a1, a0, aem, nf, order, ev_op_iterations) - - # if method == "perturbative-exact": - # return eko_perturbative( - # gamma_singlet, a1, a0, nf, order, ev_op_iterations, ev_op_max_order, True - # ) - # if method == "perturbative-expanded": - # return eko_perturbative( - # gamma_singlet, a1, a0, nf, order, ev_op_iterations, ev_op_max_order, False - # ) - # if method in ["truncated", "ordered-truncated"]: - # return eko_truncated(gamma_singlet, a1, a0, nf, order, ev_op_iterations) - # # These methods are scattered for nlo and nnlo - # if method == "decompose-exact": - # if order[0] == 2: - # return nlo_decompose_exact(gamma_singlet, a1, a0, nf) - # return nnlo_decompose_exact(gamma_singlet, a1, a0, nf) - # if method == "decompose-expanded": - # if order[0] == 2: - # return nlo_decompose_expanded(gamma_singlet, a1, a0, nf) - # return nnlo_decompose_expanded(gamma_singlet, a1, a0, nf) raise NotImplementedError("Selected method is not implemented") diff --git a/src/eko/kernels/QEDvalence.py b/src/eko/kernels/QEDvalence.py index 1a933f31f..139296414 100644 --- a/src/eko/kernels/QEDvalence.py +++ b/src/eko/kernels/QEDvalence.py @@ -11,16 +11,18 @@ @nb.njit(cache=True) def eko_iterate(gamma_valence, a1, a0, aem, nf, order, ev_op_iterations): """ - Singlet NLO or NNLO iterated (exact) EKO. + Valence iterated (exact) EKO. Parameters ---------- - gamma_singlet : numpy.ndarray - singlet anomalous dimensions matrices + gamma_valence : numpy.ndarray + valence anomalous dimensions matrices a1 : float target coupling value a0 : float initial coupling value + aem : float + electromagnetic coupling value nf : int number of active flavors order : int @@ -30,8 +32,8 @@ def eko_iterate(gamma_valence, a1, a0, aem, nf, order, ev_op_iterations): Returns ------- - e_s^{order} : numpy.ndarray - singlet NLO or NNLO iterated (exact) EKO + e_v^{order} : numpy.ndarray + Valence iterated (exact) EKO """ a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) e = np.identity(2, np.complex_) @@ -77,6 +79,8 @@ def dispatcher( # pylint: disable=too-many-return-statements target coupling value a0 : float initial coupling value + aem : float + electromagnetic coupling value nf : int number of active flavors ev_op_iterations : int @@ -86,33 +90,9 @@ def dispatcher( # pylint: disable=too-many-return-statements Returns ------- - e_s : numpy.ndarray + e_v : numpy.ndarray singlet EKO """ - # use always exact in LO - # if order == (1,0): - # return lo_exact(gamma_singlet, a1, a0, nf) - if method in ["iterate-exact", "iterate-expanded"]: return eko_iterate(gamma_valence, a1, a0, aem, nf, order, ev_op_iterations) - - # if method == "perturbative-exact": - # return eko_perturbative( - # gamma_singlet, a1, a0, nf, order, ev_op_iterations, ev_op_max_order, True - # ) - # if method == "perturbative-expanded": - # return eko_perturbative( - # gamma_singlet, a1, a0, nf, order, ev_op_iterations, ev_op_max_order, False - # ) - # if method in ["truncated", "ordered-truncated"]: - # return eko_truncated(gamma_singlet, a1, a0, nf, order, ev_op_iterations) - # # These methods are scattered for nlo and nnlo - # if method == "decompose-exact": - # if order[0] == 2: - # return nlo_decompose_exact(gamma_singlet, a1, a0, nf) - # return nnlo_decompose_exact(gamma_singlet, a1, a0, nf) - # if method == "decompose-expanded": - # if order[0] == 2: - # return nlo_decompose_expanded(gamma_singlet, a1, a0, nf) - # return nnlo_decompose_expanded(gamma_singlet, a1, a0, nf) raise NotImplementedError("Selected method is not implemented") From cfe7a290c7352a4c4c581ac8c03ee91eab92c35e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 26 Sep 2022 12:02:59 +0200 Subject: [PATCH 162/312] Fix documentation in kernels/QEDnonsinglet --- src/eko/kernels/QEDnon_singlet.py | 38 +++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/src/eko/kernels/QEDnon_singlet.py b/src/eko/kernels/QEDnon_singlet.py index 5cf48ca47..0a8f36063 100644 --- a/src/eko/kernels/QEDnon_singlet.py +++ b/src/eko/kernels/QEDnon_singlet.py @@ -11,7 +11,7 @@ @nb.njit(cache=True) def lo_aem1_exact(gamma_ns, a1, a0, aem, nf): # lo refers to the order in as1 """ - |LO| non-singlet exact EKO. + O(as1aem1) non-singlet exact EKO. Parameters ---------- @@ -21,13 +21,15 @@ def lo_aem1_exact(gamma_ns, a1, a0, aem, nf): # lo refers to the order in as1 target coupling value a0 : float initial coupling value + aem : float + electromagnetic coupling value nf : int number of active flavors Returns ------- e_ns^0 : complex - |LO| non-singlet exact EKO + O(as1aem1) non-singlet exact EKO """ return np.exp( (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j00_qed(a1, a0, aem, nf) @@ -38,7 +40,7 @@ def lo_aem1_exact(gamma_ns, a1, a0, aem, nf): # lo refers to the order in as1 @nb.njit(cache=True) def lo_aem2_exact(gamma_ns, a1, a0, aem, nf): # lo refers to the order in as1 """ - |LO| non-singlet exact EKO. + O(as1aem2) non-singlet exact EKO. Parameters ---------- @@ -48,13 +50,15 @@ def lo_aem2_exact(gamma_ns, a1, a0, aem, nf): # lo refers to the order in as1 target coupling value a0 : float initial coupling value + aem : float + electromagnetic coupling value nf : int number of active flavors Returns ------- e_ns^0 : complex - |LO| non-singlet exact EKO + O(as1aem2) non-singlet exact EKO """ return np.exp( (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j00_qed(a1, a0, aem, nf) @@ -65,7 +69,7 @@ def lo_aem2_exact(gamma_ns, a1, a0, aem, nf): # lo refers to the order in as1 @nb.njit(cache=True) def nlo_aem1_exact(gamma_ns, a1, a0, aem, nf): """ - |NLO| non-singlet exact EKO. + O(as2aem1) non-singlet exact EKO. Parameters ---------- @@ -75,13 +79,15 @@ def nlo_aem1_exact(gamma_ns, a1, a0, aem, nf): target coupling value a0 : float initial coupling value + aem : float + electromagnetic coupling value nf : int number of active flavors Returns ------- e_ns^1 : complex - |NLO| non-singlet exact EKO + O(as2aem1) non-singlet exact EKO """ return np.exp( (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j01_exact_qed(a1, a0, aem, nf) @@ -93,7 +99,7 @@ def nlo_aem1_exact(gamma_ns, a1, a0, aem, nf): @nb.njit(cache=True) def nlo_aem2_exact(gamma_ns, a1, a0, aem, nf): """ - |NLO| non-singlet exact EKO. + O(as2aem2) non-singlet exact EKO. Parameters ---------- @@ -103,13 +109,15 @@ def nlo_aem2_exact(gamma_ns, a1, a0, aem, nf): target coupling value a0 : float initial coupling value + aem : float + electromagnetic coupling value nf : int number of active flavors Returns ------- e_ns^1 : complex - |NLO| non-singlet exact EKO + O(as2aem2) non-singlet exact EKO """ return np.exp( (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j01_exact_qed(a1, a0, aem, nf) @@ -122,7 +130,7 @@ def nlo_aem2_exact(gamma_ns, a1, a0, aem, nf): @nb.njit(cache=True) def nnlo_aem1_exact(gamma_ns, a1, a0, aem, nf): """ - |NNLO| non-singlet exact EKO. + O(as3aem1) non-singlet exact EKO. Parameters ---------- @@ -132,13 +140,15 @@ def nnlo_aem1_exact(gamma_ns, a1, a0, aem, nf): target coupling value a0 : float initial coupling value + aem : float + electromagnetic coupling value nf : int number of active flavors Returns ------- e_ns^2 : complex - |NNLO| non-singlet exact EKO + O(as3aem1) non-singlet exact EKO """ return np.exp( (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j02_exact_qed(a1, a0, aem, nf) @@ -151,7 +161,7 @@ def nnlo_aem1_exact(gamma_ns, a1, a0, aem, nf): @nb.njit(cache=True) def nnlo_aem2_exact(gamma_ns, a1, a0, aem, nf): """ - |NNLO| non-singlet exact EKO. + O(as3aem2) non-singlet exact EKO. Parameters ---------- @@ -161,13 +171,15 @@ def nnlo_aem2_exact(gamma_ns, a1, a0, aem, nf): target coupling value a0 : float initial coupling value + aem : float + electromagnetic coupling value nf : int number of active flavors Returns ------- e_ns^2 : complex - |NNLO| non-singlet exact EKO + O(as3aem2) non-singlet exact EKO """ return np.exp( (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j02_exact_qed(a1, a0, aem, nf) @@ -199,6 +211,8 @@ def dispatcher( target coupling value a0 : float initial coupling value + aem : float + electromagnetic coupling value nf : int number of active flavors ev_op_iterations : int From d9e1c2e757d43dcda8c5c38fab3427b50a3986a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 26 Sep 2022 12:17:55 +0200 Subject: [PATCH 163/312] Updating docstring in evolution_integrals.py --- src/eko/kernels/evolution_integrals.py | 37 ++++++++++++++++++-------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/eko/kernels/evolution_integrals.py b/src/eko/kernels/evolution_integrals.py index 4e33212be..d52724d4c 100644 --- a/src/eko/kernels/evolution_integrals.py +++ b/src/eko/kernels/evolution_integrals.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- -r""" +r"""Compute evolution integrals. + Integrals needed for the exact evolutions are given by: .. math:: @@ -44,11 +45,11 @@ def j00(a1, a0, nf): @nb.njit(cache=True) def j00_qed(a1, a0, aem, nf): r""" - LO-LO exact evolution integral. + LO-LO QED exact evolution integral. .. math:: - j^{(0,0)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{\beta_0 a_s'} - = \frac{\ln(a_s/a_s^0)}{\beta_0} + j^{(0,0)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{(\beta_0 + aem \beta_{0,1}) a_s'} + = \frac{\ln(a_s/a_s^0)}{\beta_0 + aem \beta_{0,1}} Parameters ---------- @@ -56,6 +57,8 @@ def j00_qed(a1, a0, aem, nf): target coupling value a0 : float initial coupling value + aem : float + electromagnetic coupling value nf : int number of active flavors @@ -71,11 +74,11 @@ def j00_qed(a1, a0, aem, nf): @nb.njit(cache=True) def jm10(a1, a0, aem, nf): r""" - LO-LO exact evolution integral. + LO-LO QED exact evolution integral. .. math:: - j^{(0,0)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{\beta_0 a_s'} - = \frac{\ln(a_s/a_s^0)}{\beta_0} + j^{(-1,0)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{(\beta_0 + aem \beta_{0,1}) a_s'^2} + = (1.0 / a0 - 1.0 / a1) / (\beta_0 + aem \beta_{0,1}) Parameters ---------- @@ -83,6 +86,8 @@ def jm10(a1, a0, aem, nf): target coupling value a0 : float initial coupling value + aem : float + electromagnetic coupling value nf : int number of active flavors @@ -131,7 +136,7 @@ def j11_exact_qed(a1, a0, aem, nf): .. math:: j^{(1,1)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s}\!da_s'\, - \frac{a_s'^2}{\beta_0 a_s'^2 + \beta_1 a_s'^3} + \frac{a_s'^2}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3} = \frac{1}{\beta_1}\ln\left(\frac{1+b_1 a_s}{1+b_1 a_s^0}\right) Parameters @@ -140,6 +145,8 @@ def j11_exact_qed(a1, a0, aem, nf): target coupling value a0 : float initial coupling value + aem : float + electromagnetic coupling value nf : int number of active flavors @@ -209,10 +216,10 @@ def j01_exact(a1, a0, nf): @nb.njit(cache=True) def j01_exact_qed(a1, a0, aem, nf): r""" - LO-NLO exact evolution integral. + LO-NLO QED exact evolution integral. .. math:: - j^{(0,1)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s}\!da_s'\, + j^{(0,1)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s}\!da_s'\, \frac{a_s'}{\beta_0 a_s'^2 + \beta_1 a_s'^3} = j^{(0,0)}(a_s,a_s^0) - b_1 j^{(1,1)}(a_s,a_s^0) @@ -222,6 +229,8 @@ def j01_exact_qed(a1, a0, aem, nf): target coupling value a0 : float initial coupling value + aem : float + electromagnetic coupling value nf : int number of active flavors @@ -357,6 +366,8 @@ def j22_exact_qed(a1, a0, aem, nf): target coupling value a0 : float initial coupling value + aem : float + electromagnetic coupling value nf : int number of active flavors @@ -431,6 +442,8 @@ def j12_exact_qed(a1, a0, aem, nf): target coupling value a0 : float initial coupling value + aem : float + electromagnetic coupling value nf : int number of active flavors @@ -497,6 +510,8 @@ def j02_exact_qed(a1, a0, aem, nf): target coupling value a0 : float initial coupling value + aem : float + electromagnetic coupling value nf : int number of active flavors @@ -517,7 +532,7 @@ def j02_exact_qed(a1, a0, aem, nf): @nb.njit(cache=True) def jm12_exact(a1, a0, aem, nf): - """ + r""" LO-NNLO exact evolution integral. .. math:: From 5e908a7ecfa3b039589f7bbb1bf007f833787257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 26 Sep 2022 14:42:32 +0200 Subject: [PATCH 164/312] Fix dos in anomalous_dimensions/__init__.py --- src/eko/anomalous_dimensions/__init__.py | 86 +++++++++++------------- 1 file changed, 41 insertions(+), 45 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index 9a778827e..5df2fb3d6 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -74,55 +74,37 @@ def exp_singlet(gamma_S): @nb.njit(cache=True) -def exp_matrix(gamma_S): +def exp_matrix(gamma): r""" - Compute the exponential and the eigensystem of the singlet anomalous dimension matrix. + Compute the exponential and the eigensystem of a matrix. Parameters ---------- - gamma_S : numpy.ndarray - singlet anomalous dimension matrix + gamma : numpy.ndarray + input matrix Returns ------- exp : numpy.ndarray - exponential of the singlet anomalous dimension matrix :math:`\gamma_{S}(N)` - lambda_p : complex - positive eigenvalue of the singlet anomalous dimension matrix - :math:`\gamma_{S}(N)` - lambda_m : complex - negative eigenvalue of the singlet anomalous dimension matrix - :math:`\gamma_{S}(N)` - e_p : numpy.ndarray - projector for the positive eigenvalue of the singlet anomalous - dimension matrix :math:`\gamma_{S}(N)` - e_m : numpy.ndarray - projector for the negative eigenvalue of the singlet anomalous - dimension matrix :math:`\gamma_{S}(N)` - - See Also - -------- - eko.anomalous_dimensions.as1.gamma_singlet : :math:`\gamma_{S}^{(0)}(N)` - eko.anomalous_dimensions.as2.gamma_singlet : :math:`\gamma_{S}^{(1)}(N)` - eko.anomalous_dimensions.as3.gamma_singlet : :math:`\gamma_{S}^{(2)}(N)` + exponential of the matrix gamma :math:`\gamma(N)` + w : numpy.ndarray + array of the eigenvalues of the matrix lambda + e : numpy.ndarray + projectors on the eigenspaces of the matrix gamma :math:`\gamma(N)` """ # compute Matrix of coefficients - w, v = np.linalg.eig(gamma_S) + w, v = np.linalg.eig(gamma) vT = np.transpose(v) - matV = vT.dot(v) + matV = vT @ v matC = np.linalg.inv(matV) # compute projectors - tmp = matC.dot(vT) - dim = gamma_S.shape[0] + tmp = matC @ vT + dim = gamma.shape[0] e = np.zeros((dim, dim, dim), np.complex_) + exp = np.zeros((dim, dim), np.complex_) # TODO check if this loop can be entirely cast to numpy for i in range(dim): e[i] = np.outer(vT[i], tmp[i]) - # TODO use correct np call - # exp = sum(e[i] * np.exp(w[i]) for i in range(dim)) - # exp = np.sum(e * np.exp(w), axis=0) - exp = np.zeros((dim, dim), np.complex_) - for i in range(dim): exp += e[i] * np.exp(w[i]) return exp, w, e @@ -264,13 +246,13 @@ def gamma_singlet(order, n, nf): @nb.njit(cache=True) def gamma_ns_qed(order, mode, n, nf): r""" - Compute the tower of the non-singlet anomalous dimensions. + Compute the grid of the QED non-singlet anomalous dimensions. Parameters ---------- order : tuple(int,int) perturbative orders - mode : 10201 | 10101 | 10200 + mode : 10102 | 10103 | 10202 | 10203 sector identifier n : complex Mellin variable @@ -280,7 +262,7 @@ def gamma_ns_qed(order, mode, n, nf): Returns ------- gamma_ns : numpy.ndarray - non-singlet anomalous dimensions + non-singlet QED anomalous dimensions See Also -------- @@ -290,6 +272,14 @@ def gamma_ns_qed(order, mode, n, nf): eko.anomalous_dimensions.as3.gamma_nsp : :math:`\gamma_{ns,+}^{(2)}(N)` eko.anomalous_dimensions.as3.gamma_nsm : :math:`\gamma_{ns,-}^{(2)}(N)` eko.anomalous_dimensions.as3.gamma_nsv : :math:`\gamma_{ns,v}^{(2)}(N)` + eko.anomalous_dimensions.aem1.gamma_ns : :math:`\gamma_{ns}^{(0,1)}(N)` + eko.anomalous_dimensions.aem1.gamma_ns : :math:`\gamma_{ns,v}^{(0,1)}(N)` + eko.anomalous_dimensions.as1aem1.gamma_nsp : :math:`\gamma_{ns,p}^{(1,1)}(N)` + eko.anomalous_dimensions.as1aem1.gamma_nsm : :math:`\gamma_{ns,m}^{(1,1)}(N)` + eko.anomalous_dimensions.aem2.gamma_nspu : :math:`\gamma_{ns,pu}^{(0,2)}(N)` + eko.anomalous_dimensions.aem2.gamma_nsmu : :math:`\gamma_{ns,mu}^{(0,2)}(N)` + eko.anomalous_dimensions.aem2.gamma_nspd : :math:`\gamma_{ns,pd}^{(0,2)}(N)` + eko.anomalous_dimensions.aem2.gamma_nsmd : :math:`\gamma_{ns,md}^{(0,2)}(N)` """ # cache the s-es max_weight = max(order) @@ -414,7 +404,7 @@ def choose_ns_ad_aem2(mode, n, nf, sx): @nb.njit(cache=True) def gamma_singlet_qed(order, n, nf): r""" - Compute the tower of the singlet anomalous dimensions matrices. + Compute the grid of the QED singlet anomalous dimensions matrices. Parameters ---------- @@ -432,9 +422,12 @@ def gamma_singlet_qed(order, n, nf): See Also -------- - eko.anomalous_dimensions.as1.gamma_singlet : :math:`\gamma_{S}^{(0)}(N)` - eko.anomalous_dimensions.as2.gamma_singlet : :math:`\gamma_{S}^{(1)}(N)` - eko.anomalous_dimensions.as3.gamma_singlet : :math:`\gamma_{S}^{(2)}(N)` + eko.anomalous_dimensions.as1.gamma_QEDsinglet : :math:`\gamma_{S}^{(0)}(N)` + eko.anomalous_dimensions.as2.gamma_QEDsinglet : :math:`\gamma_{S}^{(1)}(N)` + eko.anomalous_dimensions.as3.gamma_QEDsinglet : :math:`\gamma_{S}^{(2)}(N)` + eko.anomalous_dimensions.aem1.gamma_singlet : :math:`\gamma_{S}^{(0,1)}(N)` + eko.anomalous_dimensions.as1aem1.gamma_singlet : :math:`\gamma_{S}^{(1,1)}(N)` + eko.anomalous_dimensions.aem2.gamma_singlet : :math:`\gamma_{S}^{(0,2)}(N)` """ # cache the s-es max_weight = max(order) @@ -462,7 +455,7 @@ def gamma_singlet_qed(order, n, nf): @nb.njit(cache=True) def gamma_valence_qed(order, n, nf): r""" - Compute the tower of the singlet anomalous dimensions matrices. + Compute the grid of the QED valence anomalous dimensions matrices. Parameters ---------- @@ -475,14 +468,17 @@ def gamma_valence_qed(order, n, nf): Returns ------- - gamma_singlet : numpy.ndarray - singlet anomalous dimensions matrices + gamma_valence : numpy.ndarray + valence anomalous dimensions matrices See Also -------- - eko.anomalous_dimensions.as1.gamma_singlet : :math:`\gamma_{S}^{(0)}(N)` - eko.anomalous_dimensions.as2.gamma_singlet : :math:`\gamma_{S}^{(1)}(N)` - eko.anomalous_dimensions.as3.gamma_singlet : :math:`\gamma_{S}^{(2)}(N)` + eko.anomalous_dimensions.as1.gamma_QEDvalence : :math:`\gamma_{V}^{(0)}(N)` + eko.anomalous_dimensions.as2.gamma_QEDvalence : :math:`\gamma_{V}^{(1)}(N)` + eko.anomalous_dimensions.as3.gamma_QEDvalence : :math:`\gamma_{V}^{(2)}(N)` + eko.anomalous_dimensions.aem1.gamma_valence : :math:`\gamma_{V}^{(0,1)}(N)` + eko.anomalous_dimensions.as1aem1.gamma_valence : :math:`\gamma_{V}^{(1,1)}(N)` + eko.anomalous_dimensions.aem2.gamma_valence : :math:`\gamma_{V}^{(0,2)}(N)` """ # cache the s-es max_weight = max(order) From f976128c002a02627137c45018aadff66d24b87f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 26 Sep 2022 15:06:22 +0200 Subject: [PATCH 165/312] Fix docstring in basis_rotation --- src/eko/basis_rotation.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/eko/basis_rotation.py b/src/eko/basis_rotation.py index 867fd695e..b88e8f327 100644 --- a/src/eko/basis_rotation.py +++ b/src/eko/basis_rotation.py @@ -74,6 +74,14 @@ "Tu8", "Vu8", ) +r""" +Sorted elements in Unified Evolution Basis as :obj:`str`. + +Definition: :ref:`here `. + +corresponding |PDF| : :math:`g, \gamma, \Sigma, \Sigma_\Delta, V, V_\Delta, T^d_3, V^d_3, +T^u_3, V^u_3, T^d_8, V^d_8, T^u_8, V^u_8` +""" evol_basis_pids = tuple( [22, 100, 21, 200] @@ -89,6 +97,7 @@ + [108 + 2, 208 + 2] + [108 + 1, 208 + 1] ) +"""|pid| representation of :data:`unified_evol_basis`.""" non_singlet_pids_map = { "ns-": 10201, From 05da03bea15986634d4075e9ca8aaf8fdbb9e618 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 26 Sep 2022 15:11:38 +0200 Subject: [PATCH 166/312] Fix docstring in evolution_operator/__init__ --- src/eko/evolution_operator/__init__.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index be7e1fbad..f18f241f2 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -54,12 +54,12 @@ def select_singlet_element(ker, mode0, mode1): @nb.njit(cache=True) def select_QEDsinglet_element(ker, mode0, mode1): """ - Select element of the singlet matrix. + Select element of the QEDsinglet matrix. Parameters ---------- ker : numpy.ndarray - singlet integration kernel + QEDsinglet integration kernel mode0 : int id for first sector element mode1 : int @@ -67,7 +67,7 @@ def select_QEDsinglet_element(ker, mode0, mode1): Returns ------- ker : complex - singlet integration kernel element + QEDsinglet integration kernel element """ if mode0 == 21: index1 = 0 @@ -91,12 +91,12 @@ def select_QEDsinglet_element(ker, mode0, mode1): @nb.njit(cache=True) def select_QEDvalence_element(ker, mode0, mode1): """ - Select element of the singlet matrix. + Select element of the QEDvalence matrix. Parameters ---------- ker : numpy.ndarray - singlet integration kernel + QEDvalence integration kernel mode0 : int id for first sector element mode1 : int @@ -104,7 +104,7 @@ def select_QEDvalence_element(ker, mode0, mode1): Returns ------- ker : complex - singlet integration kernel element + QEDvalence integration kernel element """ index1 = 0 if mode0 == 10200 else 1 index2 = 0 if mode1 == 10200 else 1 @@ -226,6 +226,8 @@ def quad_ker( target coupling value a0 : float initial coupling value + aem : float + electromagnetic coupling value nf : int number of active flavors L : float From 3b8bcccb01e54d33c9742605f13b4e6979c9a6ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 26 Sep 2022 15:19:09 +0200 Subject: [PATCH 167/312] Add qed docstring in functions that use it --- src/eko/basis_rotation.py | 4 ++++ src/eko/evolution_operator/flavors.py | 7 ++++++- src/eko/evolution_operator/physical.py | 7 +++++++ src/eko/matching_conditions/__init__.py | 2 ++ src/ekomark/apply.py | 2 ++ 5 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/eko/basis_rotation.py b/src/eko/basis_rotation.py index b88e8f327..1f463bdc8 100644 --- a/src/eko/basis_rotation.py +++ b/src/eko/basis_rotation.py @@ -281,6 +281,8 @@ def ad_projector(ad_lab, nf, qed=False): name of anomalous dimension sector nf : int number of light flavors + qed : bool + activate qed Returns ------- @@ -324,6 +326,8 @@ def ad_projectors(nf, qed=False): ---------- nf : int number of light flavors + qed : bool + activate qed Returns ------- diff --git a/src/eko/evolution_operator/flavors.py b/src/eko/evolution_operator/flavors.py index e56361817..6302dbca7 100644 --- a/src/eko/evolution_operator/flavors.py +++ b/src/eko/evolution_operator/flavors.py @@ -60,6 +60,11 @@ def get_range(evol_labels, qed=False): Here, we assume that the T distributions (e.g. T15) appears *always* before the corresponding V distribution (e.g. V15). + Parameters + ---------- + qed : bool + activate qed + Returns ------- nf_in : int @@ -237,7 +242,7 @@ def rotation_parameters(nf): Returns ------- a,b,c,d,e,f : float - Parameters of the rotation: Sdelta = a*S + b*Sdelta + c*h+ + Parameters of the rotation: Sdelta = a*S + b*Sdelta + c*h+, T_i = d*S + e*Sdelta + f*h+ """ nu_l = constants.uplike_flavors(nf - 1) nd_l = (nf - 1) - nu_l diff --git a/src/eko/evolution_operator/physical.py b/src/eko/evolution_operator/physical.py index 8a6337631..f74339fc2 100644 --- a/src/eko/evolution_operator/physical.py +++ b/src/eko/evolution_operator/physical.py @@ -40,6 +40,8 @@ def ad_to_evol_map(cls, op_members, nf, q2_final, intrinsic_range, qed=False): number of active light flavors intrinsic_range : sequence intrinsic heavy flavors + qed : bool + activate qed Returns ------- @@ -112,6 +114,11 @@ def to_flavor_basis_tensor(self, qed=False): """ Convert the computations into an rank 4 tensor over flavor operator space and momentum fraction operator space. + Parameters + ---------- + qed : bool + activate qed + Returns ------- tensor : numpy.ndarray diff --git a/src/eko/matching_conditions/__init__.py b/src/eko/matching_conditions/__init__.py index f1a036ead..8a289f70b 100644 --- a/src/eko/matching_conditions/__init__.py +++ b/src/eko/matching_conditions/__init__.py @@ -38,6 +38,8 @@ def split_ad_to_evol_map( threshold value intrinsic_range : list list of intrinsic quark pids + qed : bool + activate qed """ m = { "S.S": op_members[(100, 100)], diff --git a/src/ekomark/apply.py b/src/ekomark/apply.py index 61d4bf237..6cabf914e 100644 --- a/src/ekomark/apply.py +++ b/src/ekomark/apply.py @@ -61,6 +61,8 @@ def apply_pdf_flavor( if given, interpolates to the pdfs given at targetgrid (instead of xgrid) flavor_rotation : np.ndarray Rotation matrix in flavor space + qed : bool + activate qed Returns ------- From 17032a39b197183ddd88f2ad760591b1225395f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 27 Sep 2022 10:15:21 +0200 Subject: [PATCH 168/312] Move apfel_bench_qed into apfel_bench --- benchmarks/NNPDF_bench.py | 2 +- benchmarks/apfel_bench.py | 88 ++++++++++++++++++++++++ benchmarks/apfel_bench_qed.py | 123 ---------------------------------- 3 files changed, 89 insertions(+), 124 deletions(-) delete mode 100644 benchmarks/apfel_bench_qed.py diff --git a/benchmarks/NNPDF_bench.py b/benchmarks/NNPDF_bench.py index 6eba3435b..72ada3a42 100644 --- a/benchmarks/NNPDF_bench.py +++ b/benchmarks/NNPDF_bench.py @@ -70,7 +70,7 @@ def benchmark_nnlo(self, Q0=1.65, Q2grid=(100,)): theory_card = { **base_theory, "PTO": 2, - "QED": 1, + "QED": 2, "Q0": Q0, } theory_card.update( diff --git a/benchmarks/apfel_bench.py b/benchmarks/apfel_bench.py index 68f087233..c7e81eaf2 100644 --- a/benchmarks/apfel_bench.py +++ b/benchmarks/apfel_bench.py @@ -188,6 +188,90 @@ def benchmark_sv(self, pto, svmode): self.run(ts, operators.build(operators.apfel_config), ["ToyLH"]) +class BenchmarkFFNS_qed(ApfelBenchmark): + """Benckmark FFNS""" + + ffns_theory = { + "Qref": 91.1870, + "mc": 1.3, + "mb": 4.75, + "mt": 172.0, + "FNS": "FFNS", + "ModEv": [ + "EXA", + # "EXP", + # "TRN", + ], + "NfFF": 5, + "kcThr": 0.0, + "kbThr": 0.0, + "ktThr": np.inf, + "Q0": 5.0, + "alphas": 0.118000, + "alphaqed": 0.007496, + } + ffns_theory = tolist(ffns_theory) + + def benchmark_plain(self, pto, qed): + """Plain configuration""" + + th = self.ffns_theory.copy() + th.update({"PTO": [pto], "QED": [qed]}) + self.skip_pdfs = lambda _theory: [ + -6, + 6, + "Tu8", + "Vu8", + ] + self.run( + cartesian_product(th), + operators.build(operators.apfel_config), + ["NNPDF31_nnlo_as_0118_luxqed"], + ) + + +class BenchmarkVFNS_qed(ApfelBenchmark): + """Benckmark FFNS""" + + vfns_theory = { + "Qref": 91.1870, + "mc": 1.3, + "mb": 4.75, + "mt": 172.0, + "FNS": "VFNS", + "ModEv": [ + "EXA", + # "EXP", + # "TRN", + ], + "NfFF": 5, + "kcThr": 0.0, + "kbThr": 0.0, + "ktThr": np.inf, + "Q0": 5.0, + "alphas": 0.118000, + "alphaqed": 0.007496, + } + vfns_theory = tolist(vfns_theory) + + def benchmark_plain(self, pto, qed): + """Plain configuration""" + + th = self.vfns_theory.copy() + th.update({"PTO": [pto], "QED": [qed]}) + self.skip_pdfs = lambda _theory: [ + -6, + 6, + "Tu8", + "Vu8", + ] + self.run( + cartesian_product(th), + operators.build(operators.apfel_config), + ["NNPDF31_nnlo_as_0118_luxqed"], + ) + + if __name__ == "__main__": obj = BenchmarkVFNS() @@ -197,3 +281,7 @@ def benchmark_sv(self, pto, svmode): obj.benchmark_sv(2, "exponentiated") # obj.benchmark_kthr(2) # obj.benchmark_msbar(2) + + # obj = BenchmarkFFNS() + # obj = BenchmarkVFNS() + # obj.benchmark_plain(2, 2) diff --git a/benchmarks/apfel_bench_qed.py b/benchmarks/apfel_bench_qed.py deleted file mode 100644 index ccb32382f..000000000 --- a/benchmarks/apfel_bench_qed.py +++ /dev/null @@ -1,123 +0,0 @@ -# -*- coding: utf-8 -*- -""" - Benchmark EKO to Apfel -""" -import numpy as np -from banana import register -from banana.data import cartesian_product - -from eko import interpolation -from ekomark.benchmark.runner import Runner -from ekomark.data import operators - -register(__file__) - -base_theory = { - "Qref": 91.1870, - "mc": 1.3, - "mb": 4.75, - "mt": 172.0, -} - - -def tolist(input_dict): - output_dict = input_dict.copy() - for key, item in output_dict.items(): - if not isinstance(item, list): - output_dict[key] = [item] - return output_dict - - -class ApfelBenchmark(Runner): - - """ - Globally set the external program to Apfel - """ - - external = "apfel" - - # Rotate to evolution basis - rotate_to_evolution_basis = False - - @staticmethod - def skip_pdfs(_theory): - # pdf to skip - return [-6, 6, "Tu8", "Vu8"] - - -class BenchmarkFFNS(ApfelBenchmark): - """Benckmark FFNS""" - - ffns_theory = base_theory.copy() - - ffns_theory.update( - { - "FNS": "FFNS", - "ModEv": [ - "EXA", - # "EXP", - # "TRN", - ], - "NfFF": 5, - "kcThr": 0.0, - "kbThr": 0.0, - "ktThr": np.inf, - "Q0": 5.0, - "alphas": 0.118000, - "alphaqed": 0.007496, - } - ) - ffns_theory = tolist(ffns_theory) - - def benchmark_plain(self, pto, qed): - """Plain configuration""" - - th = self.ffns_theory.copy() - th.update({"PTO": [pto], "QED": [qed]}) - self.run( - cartesian_product(th), - operators.build(operators.apfel_config), - ["NNPDF31_nnlo_as_0118_luxqed"], - ) - - -class BenchmarkVFNS(ApfelBenchmark): - """Benckmark VFNS""" - - vfns_theory = base_theory.copy() - - vfns_theory.update( - { - "FNS": "VFNS", - "ModEv": [ - "EXA", - # "EXP", - # "TRN", - ], - "kcThr": 1.0, - "kbThr": 1.0, - "ktThr": np.inf, - "Q0": 5.0, - "alphas": 0.118000, - "alphaqed": 0.007496, - } - ) - ffns_theory = tolist(vfns_theory) - - def benchmark_plain(self, pto, qed): - """Plain configuration""" - - th = self.ffns_theory.copy() - th.update({"PTO": [pto], "QED": [qed]}) - self.run( - cartesian_product(th), - operators.build(operators.apfel_config), - ["NNPDF23_nnlo_as_0118_qed"], - ) - - -if __name__ == "__main__": - - obj = BenchmarkFFNS() - - obj.benchmark_plain(2, 2) From 8c811171f1eea157442210627085f56af75cdc2c Mon Sep 17 00:00:00 2001 From: niclaurenti Date: Tue, 27 Sep 2022 10:38:08 +0200 Subject: [PATCH 169/312] Fix typos in BenchmarkVFNS_qed --- benchmarks/apfel_bench.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/benchmarks/apfel_bench.py b/benchmarks/apfel_bench.py index c7e81eaf2..9b6a1d646 100644 --- a/benchmarks/apfel_bench.py +++ b/benchmarks/apfel_bench.py @@ -244,11 +244,10 @@ class BenchmarkVFNS_qed(ApfelBenchmark): # "EXP", # "TRN", ], - "NfFF": 5, - "kcThr": 0.0, - "kbThr": 0.0, - "ktThr": np.inf, - "Q0": 5.0, + "kcThr": 1.0, + "kbThr": 1.0, + "ktThr": 1.0, + "Q0": 1.25, "alphas": 0.118000, "alphaqed": 0.007496, } @@ -274,14 +273,14 @@ def benchmark_plain(self, pto, qed): if __name__ == "__main__": - obj = BenchmarkVFNS() + # obj = BenchmarkVFNS() # obj = BenchmarkFFNS() # obj.benchmark_plain(2) - obj.benchmark_sv(2, "exponentiated") + # obj.benchmark_sv(2, "exponentiated") # obj.benchmark_kthr(2) # obj.benchmark_msbar(2) - # obj = BenchmarkFFNS() - # obj = BenchmarkVFNS() - # obj.benchmark_plain(2, 2) + # obj = BenchmarkFFNS_qed() + obj = BenchmarkVFNS_qed() + obj.benchmark_plain(2, 2) From 2aacb334f759b7555a4b086927257885c9d30d5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 27 Sep 2022 16:15:09 +0200 Subject: [PATCH 170/312] Update docstrings in evolution_integrals.py --- src/eko/anomalous_dimensions/as1aem1.py | 1 - src/eko/kernels/evolution_integrals.py | 27 ++++++++++++------------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/eko/anomalous_dimensions/as1aem1.py b/src/eko/anomalous_dimensions/as1aem1.py index fdb8531e4..10d6f976c 100644 --- a/src/eko/anomalous_dimensions/as1aem1.py +++ b/src/eko/anomalous_dimensions/as1aem1.py @@ -101,7 +101,6 @@ def gamma_qph(N, nf, sx): ) -# TODO : check the Nc factors in gg gph phg phph @nb.njit(cache=True) def gamma_gph(N): r"""Compute the O(as1aem1) gluon-photon anomalous dimension. diff --git a/src/eko/kernels/evolution_integrals.py b/src/eko/kernels/evolution_integrals.py index d52724d4c..dab6449d6 100644 --- a/src/eko/kernels/evolution_integrals.py +++ b/src/eko/kernels/evolution_integrals.py @@ -78,7 +78,7 @@ def jm10(a1, a0, aem, nf): .. math:: j^{(-1,0)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{(\beta_0 + aem \beta_{0,1}) a_s'^2} - = (1.0 / a0 - 1.0 / a1) / (\beta_0 + aem \beta_{0,1}) + = \frac{1.0 / a0 - 1.0 / as}{\beta_0 + aem \beta_{0,1}} Parameters ---------- @@ -220,8 +220,8 @@ def j01_exact_qed(a1, a0, aem, nf): .. math:: j^{(0,1)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s}\!da_s'\, - \frac{a_s'}{\beta_0 a_s'^2 + \beta_1 a_s'^3} - = j^{(0,0)}(a_s,a_s^0) - b_1 j^{(1,1)}(a_s,a_s^0) + \frac{a_s'}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3} + = j^{(0,0)}(a_s,a_s^0,aem) - b_1 j^{(1,1)}(a_s,a_s^0,aem) Parameters ---------- @@ -276,9 +276,8 @@ def jm11_exact(a1, a0, aem, nf): LO-NLO exact evolution integral. .. math:: - j^{(0,1)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s}\!da_s'\, - \frac{a_s'}{\beta_0 a_s'^2 + \beta_1 a_s'^3} - = j^{(0,0)}(a_s,a_s^0) - b_1 j^{(1,1)}(a_s,a_s^0) + j^{(-1,1)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3} + = \frac{1.0 / a0 - 1.0 / as}{\beta_0 + aem \beta_{0,1}} + \frac{b_1}{(\beta_0 + aem \beta_{0,1}} \left(\log(1 + 1 / (as b_1)) - \log(1 + 1 / (a0 b_1)\right) Parameters ---------- @@ -350,9 +349,9 @@ def j22_exact_qed(a1, a0, aem, nf): NNLO-NNLO exact evolution integral. .. math:: - j^{(2,2)}(a_s,a_s^0) &= + j^{(2,2)}(a_s,a_s^0,aem) &= \int\limits_{a_s^0}^{a_s}\!da_s'\,\frac{a_s'^3} - {\beta_0 a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4} + {(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4} = \frac{1}{\beta_2}\ln\left( \frac{1 + a_s ( b_1 + b_2 a_s ) }{ 1 + a_s^0 ( b_1 + b_2 a_s^0 )}\right) - \frac{b_1 \delta}{ \beta_2 \Delta} \\ @@ -431,7 +430,7 @@ def j12_exact_qed(a1, a0, aem, nf): NLO-NNLO exact evolution integral. .. math:: - j^{(1,2)}(a_s,a_s^0) &= \int\limits_{a_s^0}^{a_s}\!da_s'\,\frac{a_s'^2}{\beta_0 a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ + j^{(1,2)}(a_s,a_s^0,aem) &= \int\limits_{a_s^0}^{a_s}\!da_s'\,\frac{a_s'^2}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ &= \frac{2 \delta}{\beta_0 \Delta} \\ \delta &= \atan \left( \frac{b_1 + 2 a_s b_2 }{ \Delta} \right) - \atan \left( \frac{b_1 + 2 a_s^0 b_2 }{ \Delta} \right) \\ \Delta &= \sqrt{4 b_2 - b_1^2} @@ -500,8 +499,8 @@ def j02_exact_qed(a1, a0, aem, nf): LO-NNLO exact evolution integral. .. math:: - j^{(0,2)}(a_s,a_s^0) &= \int\limits_{a_s^0}^{a_s}\!da_s'\, - \frac{a_s'}{\beta_0 a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ + j^{(0,2)}(a_s,a_s^0,aem) &= \int\limits_{a_s^0}^{a_s}\!da_s'\, + \frac{a_s'}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ &= j^{(0,0)}(a_s,a_s^0) - b_1 j^{(1,2)}(a_s,a_s^0) - b_2 j^{(2,2)}(a_s,a_s^0) Parameters @@ -536,9 +535,9 @@ def jm12_exact(a1, a0, aem, nf): LO-NNLO exact evolution integral. .. math:: - j^{(0,2)}(a_s,a_s^0) &= \\int\\limits_{a_s^0}^{a_s}\\!da_s'\\, - \frac{a_s'}{\beta_0 a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ - &= j^{(0,0)}(a_s,a_s^0) - b_1 j^{(1,2)}(a_s,a_s^0) - b_2 j^{(2,2)}(a_s,a_s^0) + j^{(-1,2)}(a_s,a_s^0,aem) &= \int\limits_{a_s^0}^{a_s}\!da_s'\, + \frac{1}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ + &= j^{(-1,0)}(a_s,a_s^0,aem) - b_1 j^{(0,2)}(a_s,a_s^0) - b_2 j^{(1,2)}(a_s,a_s^0) Parameters ---------- From 4388a3332def8f306108d0bf25aa61e9c46bea76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 27 Sep 2022 17:16:08 +0200 Subject: [PATCH 171/312] Fix ad_projector for qed --- src/eko/basis_rotation.py | 47 +++++++++++++++++++++++++++----- tests/eko/test_basis_rotation.py | 19 +++++++++++++ 2 files changed, 59 insertions(+), 7 deletions(-) diff --git a/src/eko/basis_rotation.py b/src/eko/basis_rotation.py index 1f463bdc8..c5768b994 100644 --- a/src/eko/basis_rotation.py +++ b/src/eko/basis_rotation.py @@ -291,19 +291,18 @@ def ad_projector(ad_lab, nf, qed=False): """ if not qed: proj = np.zeros_like(rotate_flavor_to_evolution, dtype=float) - sector = map_ad_to_evolution[ad_lab] + # restrict the evolution basis to light flavors + # NOTE: the cut is only needed for "NS_p" and "NS_m", but the other lists + # are 1-long so they are unaffected + sector = map_ad_to_evolution[ad_lab][: (nf - 1)] basis = evol_basis rotate = rotate_flavor_to_evolution.copy() else: proj = np.zeros_like(rotate_flavor_to_unified_evolution, dtype=float) - sector = map_ad_to_unified_evolution[ad_lab] + # restrict the evolution basis to light flavors + sector = select_light_flavors_uni_ev(ad_lab, nf) basis = unified_evol_basis rotate = rotate_flavor_to_unified_evolution.copy() - # restrict the evolution basis to light flavors - # NOTE: the cut is only needed for "NS_p" and "NS_m", but the other lists - # are 1-long so they are unaffected - # TODO : check this for unified evol - sector = sector[: (nf - 1)] for el in sector: out_name, in_name = el.split(".") @@ -318,6 +317,40 @@ def ad_projector(ad_lab, nf, qed=False): return proj +def select_light_flavors_uni_ev(ad_lab, nf): + """ + Select light flavors for a given ad_lab. + + Parameters + ---------- + ad_lab : str + name of anomalous dimension sector + nf : int + number of light flavors + + Returns + ------- + sector : list + list of sector for a given ad_lab + """ + if ad_lab[0] in [non_singlet_pids_map["ns+d"], non_singlet_pids_map["ns-d"]]: + if nf < 5: + # return only Td3 or Vd3 + return [map_ad_to_unified_evolution[ad_lab][0]] + else: + return map_ad_to_unified_evolution[ad_lab] + elif ad_lab[0] in [non_singlet_pids_map["ns+u"], non_singlet_pids_map["ns-u"]]: + if nf < 4: + return [] + elif nf < 6 and nf >= 4: + # return only Td3 or Vd3 + return [map_ad_to_unified_evolution[ad_lab][0]] + else: + return map_ad_to_unified_evolution[ad_lab] + else: + return map_ad_to_evolution[ad_lab] + + def ad_projectors(nf, qed=False): """ Build projectors tensor (as a numpy array), collecting all the individual sector projectors. diff --git a/tests/eko/test_basis_rotation.py b/tests/eko/test_basis_rotation.py index b9d0c5da9..74fc9f511 100644 --- a/tests/eko/test_basis_rotation.py +++ b/tests/eko/test_basis_rotation.py @@ -33,6 +33,9 @@ def test_ad_projector_qed(): s = br.rotate_flavor_to_unified_evolution[2] g = br.rotate_flavor_to_unified_evolution[0] vd3 = br.rotate_flavor_to_unified_evolution[br.unified_evol_basis.index("Vd3")] + vd8 = br.rotate_flavor_to_unified_evolution[br.unified_evol_basis.index("Vd8")] + vu3 = br.rotate_flavor_to_unified_evolution[br.unified_evol_basis.index("Vu3")] + vu8 = br.rotate_flavor_to_unified_evolution[br.unified_evol_basis.index("Vu8")] s_to_s = br.ad_projector((100, 100), nf=6, qed=True) # import pdb; pdb.set_trace() @@ -51,6 +54,22 @@ def test_ad_projector_qed(): np.testing.assert_allclose(s @ ns_md, 0.0, atol=1e-15) np.testing.assert_allclose(g @ ns_md, 0.0) np.testing.assert_allclose(vd3 @ ns_md, vd3) + np.testing.assert_allclose(vd8 @ ns_md, vd8) + + ns_md = br.ad_projector((br.non_singlet_pids_map["ns-d"], 0), nf=3, qed=True) + ns_mu = br.ad_projector((br.non_singlet_pids_map["ns-u"], 0), nf=3, qed=True) + np.testing.assert_allclose(vd3 @ ns_md, vd3) + np.testing.assert_allclose(vd8 @ ns_md, 0.0) + np.testing.assert_allclose(vu3 @ ns_mu, 0.0) + np.testing.assert_allclose(vu8 @ ns_mu, 0.0) + + ns_mu = br.ad_projector((br.non_singlet_pids_map["ns-u"], 0), nf=4, qed=True) + np.testing.assert_allclose(vu3 @ ns_mu, vu3) + np.testing.assert_allclose(vu8 @ ns_mu, 0.0) + + ns_mu = br.ad_projector((br.non_singlet_pids_map["ns-u"], 0), nf=6, qed=True) + np.testing.assert_allclose(vu3 @ ns_mu, vu3) + np.testing.assert_allclose(vu8 @ ns_mu, vu8) def test_ad_projectors(): From 3698ee7ca395c45dd9b1c95b79082c8e92d6c331 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 3 Oct 2022 14:33:28 +0200 Subject: [PATCH 172/312] Implementing solution with running alpha --- src/eko/couplings.py | 64 ++++++++++++++++++- src/eko/evolution_operator/__init__.py | 32 +++++++--- src/eko/evolution_operator/grid.py | 22 +++---- src/eko/kernels/QEDnon_singlet.py | 3 +- src/eko/kernels/QEDsinglet.py | 20 ++++-- src/eko/kernels/QEDvalence.py | 20 ++++-- .../operator_matrix_element.py | 2 +- tests/eko/test_basis_rotation.py | 1 - tests/eko/test_ev_op_flavors.py | 1 - 9 files changed, 127 insertions(+), 38 deletions(-) diff --git a/src/eko/couplings.py b/src/eko/couplings.py index f86d9cae1..709d94c66 100644 --- a/src/eko/couplings.py +++ b/src/eko/couplings.py @@ -624,7 +624,7 @@ def compute_exact_running_alphaem(self, a_ref, nf, scale_from, scale_to): if self.order[1] >= 2: beta_qed_vec.append(beta_qed((0, 3), nf)) # integration kernel - def rge(_t, a, beta_qcd_vec, beta_qed_vec): + def rge(_t, a, beta_qcd_vec, beta_qcd_mix, beta_qed_vec, beta_qed_mix): rge_qcd = -(a[0] ** 2) * ( np.sum([a[0] ** k * b for k, b in enumerate(beta_qcd_vec)]) + a[1] * beta_qcd_mix @@ -641,7 +641,7 @@ def rge(_t, a, beta_qcd_vec, beta_qed_vec): rge, (0, u), a_ref, - args=[beta_qcd_vec, beta_qed_vec], + args=[beta_qcd_vec, beta_qcd_mix, beta_qed_vec, beta_qed_mix], method="Radau", rtol=1e-6, ) @@ -697,6 +697,66 @@ def compute_exact_fixed_alphaem(self, a_ref, nf, scale_from, scale_to): ) return np.array([rge_qcd, a_ref[1]]) + def compute_aem_as(self, as_to, nf): + """Compute :math:`a_{em}` as a function of :math:`a_s`. + + Parameters + ---------- + as_to : float + target a_s + nf : int + value of nf for computing alpha_i + + Returns + ------- + float + a_em at target a_s :math:`a_em(a_s)` + """ + if not self.running_alphaem: + return self.a_ref[1] + beta_qcd_vec = [beta_qcd((2, 0), nf)] + beta_qed_vec = [] + beta_qcd_mix = 0 + beta_qed_mix = 0 + # NLO + if self.order[0] >= 2: + beta_qcd_vec.append(beta_qcd((3, 0), nf)) + # NNLO + if self.order[0] >= 3: + beta_qcd_vec.append(beta_qcd((4, 0), nf)) + # N3LO + if self.order[0] >= 4: + beta_qcd_vec.append(beta_qcd((5, 0), nf)) + if self.order[1] >= 1: + beta_qed_vec = [beta_qed((0, 2), nf)] + beta_qcd_mix = beta_qcd((2, 1), nf) + beta_qed_mix = beta_qed((1, 2), nf) # order[0] is always at least 1 + if self.order[1] >= 2: + beta_qed_vec.append(beta_qed((0, 3), nf)) + + def rge(_as, a_em, beta_qcd_vec, beta_qcd_mix, beta_qed_vec, beta_qed_mix): + rge_qed = -( + a_em**2 + * (np.sum([a_em**k * b for k, b in enumerate(beta_qed_vec)])) + + beta_qed_mix * _as + ) + rge_qcd = -( + _as**2 * (np.sum([_as**k * b for k, b in enumerate(beta_qcd_vec)])) + + beta_qcd_mix * a_em + ) + + return rge_qed / rge_qcd + + res = scipy.integrate.solve_ivp( + rge, + (self.a_ref[0], as_to), + (self.a_ref[1],), + args=[beta_qcd_vec, beta_qcd_mix, beta_qed_vec, beta_qed_mix], + method="Radau", + rtol=1e-6, + ) + return res.y[0][-1] + def compute(self, a_ref, nf, scale_from, scale_to): """Compute actual couplings. diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index f18f241f2..608bb3f84 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -23,6 +23,7 @@ from ..kernels import QEDvalence as qed_v from ..kernels import non_singlet as ns from ..kernels import singlet as s +from ..kernels import utils from ..member import OpMember logger = logging.getLogger(__name__) @@ -194,7 +195,7 @@ def quad_ker( areas, as1, as0, - aem, + aem_list, nf, L, ev_op_iterations, @@ -310,7 +311,7 @@ def quad_ker( gamma_s, as1, as0, - aem, + aem_list, nf, ev_op_iterations, ev_op_max_order, @@ -335,7 +336,7 @@ def quad_ker( gamma_v, as1, as0, - aem, + aem_list, nf, ev_op_iterations, ev_op_max_order, @@ -359,7 +360,7 @@ def quad_ker( gamma_ns, as1, as0, - aem, + aem_list, nf, ev_op_iterations, ) @@ -458,13 +459,26 @@ def mur2_shift(self, q2): @property def a_s(self): """Return the computed values for :math:`a_s`.""" - sc = self.managers["strong_coupling"] + sc = self.managers["couplings"] a0 = sc.a_s( self.mur2_shift(self.q2_from), fact_scale=self.q2_from, nf_to=self.nf ) a1 = sc.a_s(self.mur2_shift(self.q2_to), fact_scale=self.q2_to, nf_to=self.nf) return (a0, a1) + def aem_list_as(self, a0, a1): + """Return the list of the couplings for the different values of :math:`a_s`.""" + sc = self.managers["couplings"] + ev_op_iterations = self.config["ev_op_iterations"] + as_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) + as_l = as_steps[0] + aem_list = [] + for as_h in as_steps[1:]: + as_half = (as_h + as_l) / 2.0 + aem_list.append(sc.compute_aem_as(as_to=as_half, nf=self.nf)) + as_l = as_h + return aem_list + @property def labels(self): """Compute necessary sector labels to compute. @@ -527,6 +541,8 @@ def quad_ker(self, label, logx, areas): partially initialized integration kernel """ + as1 = self.a_s[1] + as0 = self.a_s[0] return functools.partial( quad_ker, order=self.order, @@ -536,9 +552,9 @@ def quad_ker(self, label, logx, areas): is_log=self.int_disp.log, logx=logx, areas=areas, - as1=self.a_s[1], - as0=self.a_s[0], - aem=self.config["alphaem"] / 4 / np.pi, + as1=as1, + as0=as0, + aem_list=self.aem_list_as(a0=as0, a1=as1), nf=self.nf, L=np.log(self.fact_to_ren), ev_op_iterations=self.config["ev_op_iterations"], diff --git a/src/eko/evolution_operator/grid.py b/src/eko/evolution_operator/grid.py index 015a1eb09..cfb654573 100644 --- a/src/eko/evolution_operator/grid.py +++ b/src/eko/evolution_operator/grid.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -"""This module contains the :class:`OperatorGrid` class. +"""Contain the :class:`OperatorGrid` class. The first is the driver class of eko as it is the one that collects all the previously instantiated information and does the actual computation of the Q2s. @@ -38,8 +38,8 @@ class OperatorGrid(sv.ModeMixin): thresholds_config: eko.thresholds.ThresholdsAtlas Instance of :class:`~eko.thresholds.Threshold` containing information about the thresholds - strong_coupling: eko.strong_coupling.StrongCoupling - Instance of :class:`~eko.strong_coupling.StrongCoupling` able to generate a_s for + couplings: eko.couplings.StrongCoupling + Instance of :class:`~eko.couplings.StrongCoupling` able to generate a_s for any q kernel_dispatcher: eko.kernel_generation.KernelDispatcher Instance of the :class:`~eko.kernel_generation.KernelDispatcher` with the @@ -51,7 +51,7 @@ def __init__( config, q2_grid, thresholds_config, - strong_coupling, + couplings, interpol_dispatcher, ): # check @@ -75,7 +75,7 @@ def __init__( self.q2_grid = q2_grid self.managers = dict( thresholds_config=thresholds_config, - strong_coupling=strong_coupling, + couplings=couplings, interpol_dispatcher=interpol_dispatcher, ) self._threshold_operators = {} @@ -87,7 +87,7 @@ def from_dict( theory_card, operators_card, thresholds_config, - strong_coupling, + couplings, interpol_dispatcher, ): """ @@ -99,7 +99,7 @@ def from_dict( theory dictionary thresholds_config : eko.thresholds.ThresholdsAtlas An instance of the ThresholdsAtlas class - strong_coupling : eko.strong_coupling.StrongCoupling + couplings : eko.couplings.StrongCoupling An instance of the StrongCoupling class interpol_dispatcher : eko.interpolation.InterpolatorDispatcher An instance of the InterpolatorDispatcher class @@ -138,9 +138,7 @@ def from_dict( config["intrinsic_range"] = intrinsic_range for hq in br.quark_names[3:]: config[f"m{hq}"] = theory_card[f"m{hq}"] - return cls( - config, q2_grid, thresholds_config, strong_coupling, interpol_dispatcher - ) + return cls(config, q2_grid, thresholds_config, couplings, interpol_dispatcher) def get_threshold_operators(self, path): """ @@ -204,7 +202,7 @@ def get_threshold_operators(self, path): return thr_ops def compute(self, q2grid=None): - """Computes all ekos for the `q2grid`. + """Compute all ekos for the `q2grid`. Parameters ---------- @@ -235,7 +233,7 @@ def compute(self, q2grid=None): return grid_return def generate(self, q2): - r"""Computes a single EKO. + r"""Compute a single EKO. Parameters ---------- diff --git a/src/eko/kernels/QEDnon_singlet.py b/src/eko/kernels/QEDnon_singlet.py index 0a8f36063..aa45fa030 100644 --- a/src/eko/kernels/QEDnon_singlet.py +++ b/src/eko/kernels/QEDnon_singlet.py @@ -192,7 +192,7 @@ def nnlo_aem2_exact(gamma_ns, a1, a0, aem, nf): @nb.njit(cache=True) def dispatcher( - order, method, gamma_ns, a1, a0, aem, nf, ev_op_iterations + order, method, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations ): # pylint: disable=too-many-return-statements """ Determine used kernel and call it. @@ -224,6 +224,7 @@ def dispatcher( non-singlet EKO """ # use always exact in LO + aem = aem_list[0] if order[1] == 0: return non_singlet.dispatcher( order, method, gamma_ns[1:, 0], a1, a0, nf, ev_op_iterations diff --git a/src/eko/kernels/QEDsinglet.py b/src/eko/kernels/QEDsinglet.py index 7f5f3fc5e..082a8f005 100644 --- a/src/eko/kernels/QEDsinglet.py +++ b/src/eko/kernels/QEDsinglet.py @@ -9,7 +9,7 @@ @nb.njit(cache=True) -def eko_iterate(gamma_singlet, a1, a0, aem, nf, order, ev_op_iterations): +def eko_iterate(gamma_singlet, a1, a0, aem_list, nf, order, ev_op_iterations): """Singlet QEDxQCD iterated (exact) EKO. Parameters @@ -41,15 +41,15 @@ def eko_iterate(gamma_singlet, a1, a0, aem, nf, order, ev_op_iterations): for i in range(1, 3 + 1): betaQCD[i, 0] = beta.beta_qcd((i + 1, 0), nf) betaQCD[1, 1] = beta.beta_qcd((2, 1), nf) - for ah in a_steps[1:]: + for (i, ah) in enumerate(a_steps[1:]): a_half = (ah + al) / 2.0 delta_a = ah - al gamma = np.zeros((4, 4), np.complex_) betatot = 0 for i in range(0, order[0] + 1): for j in range(0, order[1] + 1): - betatot += a_half**1 * betaQCD[i, j] * a_half**i * aem**j - gamma += gamma_singlet[i, j] * a_half**i * aem**j + betatot += a_half**1 * betaQCD[i, j] * a_half**i * aem_list[i] ** j + gamma += gamma_singlet[i, j] * a_half**i * aem_list[i] ** j ln = gamma / betatot * delta_a ek = np.ascontiguousarray(ad.exp_matrix(ln)[0]) e = ek @ e @@ -59,7 +59,15 @@ def eko_iterate(gamma_singlet, a1, a0, aem, nf, order, ev_op_iterations): @nb.njit(cache=True) def dispatcher( # pylint: disable=too-many-return-statements - order, method, gamma_singlet, a1, a0, aem, nf, ev_op_iterations, ev_op_max_order + order, + method, + gamma_singlet, + a1, + a0, + aem_list, + nf, + ev_op_iterations, + ev_op_max_order, ): """ Determine used kernel and call it. @@ -91,5 +99,5 @@ def dispatcher( # pylint: disable=too-many-return-statements singlet EKO """ if method in ["iterate-exact", "iterate-expanded"]: - return eko_iterate(gamma_singlet, a1, a0, aem, nf, order, ev_op_iterations) + return eko_iterate(gamma_singlet, a1, a0, aem_list, nf, order, ev_op_iterations) raise NotImplementedError("Selected method is not implemented") diff --git a/src/eko/kernels/QEDvalence.py b/src/eko/kernels/QEDvalence.py index 139296414..37e191caa 100644 --- a/src/eko/kernels/QEDvalence.py +++ b/src/eko/kernels/QEDvalence.py @@ -9,7 +9,7 @@ @nb.njit(cache=True) -def eko_iterate(gamma_valence, a1, a0, aem, nf, order, ev_op_iterations): +def eko_iterate(gamma_valence, a1, a0, aem_list, nf, order, ev_op_iterations): """ Valence iterated (exact) EKO. @@ -42,15 +42,15 @@ def eko_iterate(gamma_valence, a1, a0, aem, nf, order, ev_op_iterations): for i in range(1, 3 + 1): betaQCD[i, 0] = beta.beta_qcd((i + 1, 0), nf) betaQCD[1, 1] = beta.beta_qcd((2, 1), nf) - for ah in a_steps[1:]: + for (i, ah) in enumerate(a_steps[1:]): a_half = (ah + al) / 2.0 delta_a = ah - al gamma = np.zeros((2, 2), np.complex_) betatot = 0 for i in range(0, order[0] + 1): for j in range(0, order[1] + 1): - betatot += a_half**1 * betaQCD[i, j] * a_half**i * aem**j - gamma += gamma_valence[i, j] * a_half**i * aem**j + betatot += a_half**1 * betaQCD[i, j] * a_half**i * aem_list[i] ** j + gamma += gamma_valence[i, j] * a_half**i * aem_list[i] ** j ln = gamma / betatot * delta_a ek = np.ascontiguousarray(ad.exp_matrix(ln)[0]) e = ek @ e @@ -60,7 +60,15 @@ def eko_iterate(gamma_valence, a1, a0, aem, nf, order, ev_op_iterations): @nb.njit(cache=True) def dispatcher( # pylint: disable=too-many-return-statements - order, method, gamma_valence, a1, a0, aem, nf, ev_op_iterations, ev_op_max_order + order, + method, + gamma_valence, + a1, + a0, + aem_list, + nf, + ev_op_iterations, + ev_op_max_order, ): """ Determine used kernel and call it. @@ -94,5 +102,5 @@ def dispatcher( # pylint: disable=too-many-return-statements singlet EKO """ if method in ["iterate-exact", "iterate-expanded"]: - return eko_iterate(gamma_valence, a1, a0, aem, nf, order, ev_op_iterations) + return eko_iterate(gamma_valence, a1, a0, aem_list, nf, order, ev_op_iterations) raise NotImplementedError("Selected method is not implemented") diff --git a/src/eko/matching_conditions/operator_matrix_element.py b/src/eko/matching_conditions/operator_matrix_element.py index 0b24f5970..a6a302368 100644 --- a/src/eko/matching_conditions/operator_matrix_element.py +++ b/src/eko/matching_conditions/operator_matrix_element.py @@ -365,7 +365,7 @@ def a_s(self): Note that here you need to use :math:`a_s^{n_f+1}` """ - sc = self.managers["strong_coupling"] + sc = self.managers["couplings"] return sc.a_s(self.mur2_shift(self.q2_from), self.q2_from, nf_to=self.nf + 1) def compute(self): diff --git a/tests/eko/test_basis_rotation.py b/tests/eko/test_basis_rotation.py index 74fc9f511..10f8c2a87 100644 --- a/tests/eko/test_basis_rotation.py +++ b/tests/eko/test_basis_rotation.py @@ -38,7 +38,6 @@ def test_ad_projector_qed(): vu8 = br.rotate_flavor_to_unified_evolution[br.unified_evol_basis.index("Vu8")] s_to_s = br.ad_projector((100, 100), nf=6, qed=True) - # import pdb; pdb.set_trace() np.testing.assert_allclose(s @ s_to_s, s) np.testing.assert_allclose(g @ s_to_s, 0.0) np.testing.assert_allclose(vd3 @ s_to_s, 0.0) diff --git a/tests/eko/test_ev_op_flavors.py b/tests/eko/test_ev_op_flavors.py index 672f74baf..afb7cd1d2 100644 --- a/tests/eko/test_ev_op_flavors.py +++ b/tests/eko/test_ev_op_flavors.py @@ -172,7 +172,6 @@ def load(m): for nf in range(4, 6 + 1): m = load(flavors.rotate_matching(nf, True)) minv = load(flavors.rotate_matching_inverse(nf, True)) - # import pdb; pdb.set_trace() print(m @ minv) np.testing.assert_allclose( m @ minv, np.eye(len(br.unified_evol_basis)), atol=1e-10 From 46e2392f7440c91158854c722a73f38f50e5a84c Mon Sep 17 00:00:00 2001 From: niclaurenti Date: Mon, 3 Oct 2022 15:18:56 +0200 Subject: [PATCH 173/312] Fixing tests --- src/eko/couplings.py | 3 ++- src/eko/evolution_operator/__init__.py | 14 +++++----- src/eko/kernels/QEDsinglet.py | 6 ++--- src/eko/kernels/QEDvalence.py | 6 ++--- tests/eko/test_ev_operator.py | 22 ++++++++-------- tests/eko/test_kernels_QEDns.py | 36 +++++++++++++------------- tests/eko/test_kernels_QEDsinglet.py | 8 +++--- tests/eko/test_kernels_QEDvalence.py | 8 +++--- 8 files changed, 52 insertions(+), 51 deletions(-) diff --git a/src/eko/couplings.py b/src/eko/couplings.py index 709d94c66..7e90c4af1 100644 --- a/src/eko/couplings.py +++ b/src/eko/couplings.py @@ -442,7 +442,8 @@ def __init__( if method not in ["expanded", "exact"]: raise ValueError(f"Unknown method {method}") self.method = method - self.running_alphaem = running_alphaem + # self.running_alphaem = running_alphaem + self.running_alphaem = True # running_alphaem is put for a future implementation of the DGLAP solution # with running alpha. For the moment in must remain set to False diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 608bb3f84..cc5e2a630 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -523,7 +523,7 @@ def labels(self): labels.extend(br.singlet_unified_labels) return labels - def quad_ker(self, label, logx, areas): + def quad_ker(self, label, logx, areas, aem_list): """Return partially initialized integrand function. Parameters @@ -541,8 +541,6 @@ def quad_ker(self, label, logx, areas): partially initialized integration kernel """ - as1 = self.a_s[1] - as0 = self.a_s[0] return functools.partial( quad_ker, order=self.order, @@ -552,9 +550,9 @@ def quad_ker(self, label, logx, areas): is_log=self.int_disp.log, logx=logx, areas=areas, - as1=as1, - as0=as0, - aem_list=self.aem_list_as(a0=as0, a1=as1), + as1=self.a_s[1], + as0=self.a_s[0], + aem_list=aem_list, nf=self.nf, L=np.log(self.fact_to_ren), ev_op_iterations=self.config["ev_op_iterations"], @@ -604,6 +602,8 @@ def run_op_integration( column = [] k, logx = log_grid start_time = time.perf_counter() + # import pdb; pdb.set_trace() + aem_list = self.aem_list_as(self.a_s[0], self.a_s[1]) # iterate basis functions for l, bf in enumerate(self.int_disp): if k == l and l == self.grid_size - 1: @@ -612,7 +612,7 @@ def run_op_integration( # iterate sectors for label in self.labels: res = integrate.quad( - self.quad_ker(label, logx, bf.areas_representation), + self.quad_ker(label, logx, bf.areas_representation, aem_list), 0.5, 1.0 - self._mellin_cut, epsabs=1e-12, diff --git a/src/eko/kernels/QEDsinglet.py b/src/eko/kernels/QEDsinglet.py index 082a8f005..07705d7b7 100644 --- a/src/eko/kernels/QEDsinglet.py +++ b/src/eko/kernels/QEDsinglet.py @@ -41,15 +41,15 @@ def eko_iterate(gamma_singlet, a1, a0, aem_list, nf, order, ev_op_iterations): for i in range(1, 3 + 1): betaQCD[i, 0] = beta.beta_qcd((i + 1, 0), nf) betaQCD[1, 1] = beta.beta_qcd((2, 1), nf) - for (i, ah) in enumerate(a_steps[1:]): + for (step, ah) in enumerate(a_steps[1:]): a_half = (ah + al) / 2.0 delta_a = ah - al gamma = np.zeros((4, 4), np.complex_) betatot = 0 for i in range(0, order[0] + 1): for j in range(0, order[1] + 1): - betatot += a_half**1 * betaQCD[i, j] * a_half**i * aem_list[i] ** j - gamma += gamma_singlet[i, j] * a_half**i * aem_list[i] ** j + betatot += a_half**1 * betaQCD[i, j] * a_half**i * aem_list[step] ** j + gamma += gamma_singlet[i, j] * a_half**i * aem_list[step] ** j ln = gamma / betatot * delta_a ek = np.ascontiguousarray(ad.exp_matrix(ln)[0]) e = ek @ e diff --git a/src/eko/kernels/QEDvalence.py b/src/eko/kernels/QEDvalence.py index 37e191caa..0d6126f30 100644 --- a/src/eko/kernels/QEDvalence.py +++ b/src/eko/kernels/QEDvalence.py @@ -42,15 +42,15 @@ def eko_iterate(gamma_valence, a1, a0, aem_list, nf, order, ev_op_iterations): for i in range(1, 3 + 1): betaQCD[i, 0] = beta.beta_qcd((i + 1, 0), nf) betaQCD[1, 1] = beta.beta_qcd((2, 1), nf) - for (i, ah) in enumerate(a_steps[1:]): + for (step, ah) in enumerate(a_steps[1:]): a_half = (ah + al) / 2.0 delta_a = ah - al gamma = np.zeros((2, 2), np.complex_) betatot = 0 for i in range(0, order[0] + 1): for j in range(0, order[1] + 1): - betatot += a_half**1 * betaQCD[i, j] * a_half**i * aem_list[i] ** j - gamma += gamma_valence[i, j] * a_half**i * aem_list[i] ** j + betatot += a_half**1 * betaQCD[i, j] * a_half**i * aem_list[step] ** j + gamma += gamma_valence[i, j] * a_half**i * aem_list[step] ** j ln = gamma / betatot * delta_a ek = np.ascontiguousarray(ad.exp_matrix(ln)[0]) e = ek @ e diff --git a/tests/eko/test_ev_operator.py b/tests/eko/test_ev_operator.py index e32f70de6..fe32754b0 100644 --- a/tests/eko/test_ev_operator.py +++ b/tests/eko/test_ev_operator.py @@ -42,7 +42,7 @@ def test_quad_ker(monkeypatch): areas=np.zeros(3), as1=1, as0=2, - aem=0.00058, + aem_list=[0.00058], nf=3, L=0, ev_op_iterations=0, @@ -62,7 +62,7 @@ def test_quad_ker(monkeypatch): areas=np.zeros(3), as1=1, as0=2, - aem=0.00058, + aem_list=[0.00058], nf=3, L=0, ev_op_iterations=0, @@ -82,7 +82,7 @@ def test_quad_ker(monkeypatch): areas=np.zeros(3), as1=1, as0=2, - aem=0.00058, + aem_list=[0.00058], nf=3, L=0, ev_op_iterations=0, @@ -102,7 +102,7 @@ def test_quad_ker(monkeypatch): areas=np.zeros(3), as1=1, as0=2, - aem=0.00058, + aem_list=[0.00058], nf=3, L=0, ev_op_iterations=0, @@ -122,7 +122,7 @@ def test_quad_ker(monkeypatch): areas=np.zeros(3), as1=1, as0=2, - aem=0.00058, + aem_list=[0.00058], nf=3, L=0, ev_op_iterations=0, @@ -142,7 +142,7 @@ def test_quad_ker(monkeypatch): areas=np.zeros(3), as1=1, as0=2, - aem=0.00058, + aem_list=[0.00058], nf=3, L=0, ev_op_iterations=0, @@ -162,7 +162,7 @@ def test_quad_ker(monkeypatch): areas=np.zeros(3), as1=1, as0=2, - aem=0.00058, + aem_list=[0.00058], nf=3, L=0, ev_op_iterations=0, @@ -182,7 +182,7 @@ def test_quad_ker(monkeypatch): areas=np.zeros(3), as1=1, as0=2, - aem=0.00058, + aem_list=[0.00058], nf=3, L=0, ev_op_iterations=0, @@ -204,7 +204,7 @@ def test_quad_ker(monkeypatch): areas=np.zeros(3), as1=1, as0=2, - aem=0.00058, + aem_list=[0.00058], nf=3, L=0, ev_op_iterations=0, @@ -234,7 +234,7 @@ def test_quad_ker(monkeypatch): areas=np.zeros(3), as1=1, as0=2, - aem=0.00058, + aem_list=[0.00058], nf=3, L=0, ev_op_iterations=0, @@ -256,7 +256,7 @@ def test_quad_ker(monkeypatch): areas=np.zeros(3), as1=1, as0=2, - aem=0.00058, + aem_list=[0.00058], nf=3, L=0, ev_op_iterations=0, diff --git a/tests/eko/test_kernels_QEDns.py b/tests/eko/test_kernels_QEDns.py index 3eebb4e4d..a24ed0f6c 100644 --- a/tests/eko/test_kernels_QEDns.py +++ b/tests/eko/test_kernels_QEDns.py @@ -34,7 +34,7 @@ def test_zero(): for method in methods: np.testing.assert_allclose( ns.dispatcher( - order, method, gamma_ns, 1.0, 1.0, 1.0, nf, ev_op_iterations + order, method, gamma_ns, 1.0, 1.0, [1.0, 1.0], nf, ev_op_iterations ), 1.0, ) @@ -45,7 +45,7 @@ def test_zero(): np.zeros((qcd + 1, qed + 1), dtype=complex), 2.0, 1.0, - 1.0, + [1.0, 1.0], nf, ev_op_iterations, ), @@ -68,7 +68,7 @@ def test_zero_true_gamma(): for method in methods: np.testing.assert_allclose( ns.dispatcher( - order, method, gamma_ns, 1.0, 1.0, 1.0, nf, ev_op_iterations + order, method, gamma_ns, 1.0, 1.0, [1.0, 1.0], nf, ev_op_iterations ), 1.0, ) @@ -79,7 +79,7 @@ def test_zero_true_gamma(): np.zeros((qcd + 1, qed + 1), dtype=complex), 2.0, 1.0, - 1.0, + [1.0, 1.0], nf, ev_op_iterations, ), @@ -88,9 +88,9 @@ def test_zero_true_gamma(): def test_ode(): - aem = 0.01 nf = 3 ev_op_iterations = 10 + aem_list = [0.01] * ev_op_iterations delta_a = -1e-6 a0 = 0.3 betaQCD = np.zeros((4, 3), np.complex_) @@ -114,13 +114,13 @@ def test_ode(): betatot = 0.0 for i in range(0, order[0] + 1): for j in range(0, order[1] + 1): - gammatot += gamma_ns[i, j] * a1**i * aem**j - betatot += a1**1 * betaQCD[i, j] * a1**i * aem**j + gammatot += gamma_ns[i, j] * a1**i * aem_list[0]**j + betatot += a1**1 * betaQCD[i, j] * a1**i * aem_list[0]**j r = gammatot / betatot for method in methods: rhs = r * ns.dispatcher( - order, method, gamma_ns, a1, a0, aem, nf, ev_op_iterations + order, method, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations ) lhs = ( ns.dispatcher( @@ -129,7 +129,7 @@ def test_ode(): gamma_ns, a1 + 0.5 * delta_a, a0, - aem, + aem_list, nf, ev_op_iterations, ) @@ -139,7 +139,7 @@ def test_ode(): gamma_ns, a1 - 0.5 * delta_a, a0, - aem, + aem_list, nf, ev_op_iterations, ) @@ -148,9 +148,9 @@ def test_ode(): def test_ode_true_gamma(): - aem = 0.01 - nf = 3 ev_op_iterations = 10 + aem_list = [0.01] * ev_op_iterations + nf = 3 delta_a = -1e-6 a0 = 0.3 betaQCD = np.zeros((4, 3), np.complex_) @@ -170,13 +170,13 @@ def test_ode_true_gamma(): betatot = 0.0 for i in range(0, order[0] + 1): for j in range(0, order[1] + 1): - gammatot += gamma_ns[i, j] * a1**i * aem**j - betatot += a1**1 * betaQCD[i, j] * a1**i * aem**j + gammatot += gamma_ns[i, j] * a1**i * aem_list[0]**j + betatot += a1**1 * betaQCD[i, j] * a1**i * aem_list[0]**j r = gammatot / betatot for method in methods: rhs = r * ns.dispatcher( - order, method, gamma_ns, a1, a0, aem, nf, ev_op_iterations + order, method, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations ) lhs = ( ns.dispatcher( @@ -185,7 +185,7 @@ def test_ode_true_gamma(): gamma_ns, a1 + 0.5 * delta_a, a0, - aem, + aem_list, nf, ev_op_iterations, ) @@ -195,7 +195,7 @@ def test_ode_true_gamma(): gamma_ns, a1 - 0.5 * delta_a, a0, - aem, + aem_list, nf, ev_op_iterations, ) @@ -206,5 +206,5 @@ def test_ode_true_gamma(): def test_error(): with pytest.raises(NotImplementedError): ns.dispatcher( - (4, 2), "iterate-exact", np.random.rand(4, 3), 0.2, 0.1, 0.01, 3, 10 + (4, 2), "iterate-exact", np.random.rand(4, 3), 0.2, 0.1, [0.01], 3, 10 ) diff --git a/tests/eko/test_kernels_QEDsinglet.py b/tests/eko/test_kernels_QEDsinglet.py index 43d3aa213..7f9e466ff 100644 --- a/tests/eko/test_kernels_QEDsinglet.py +++ b/tests/eko/test_kernels_QEDsinglet.py @@ -52,7 +52,7 @@ def test_zero(monkeypatch): gamma_s, 1, 1, - 1, + [1,1], nf, ev_op_iterations, ev_op_max_order, @@ -66,7 +66,7 @@ def test_zero(monkeypatch): np.zeros((qcd + 1, qed + 1, 4, 4), dtype=complex), 2, 1, - 1, + [1,1], nf, ev_op_iterations, ev_op_max_order, @@ -106,7 +106,7 @@ def test_zero_true_gamma(monkeypatch): gamma_s, 1, 1, - 1, + [1,1], nf, ev_op_iterations, ev_op_max_order, @@ -120,7 +120,7 @@ def test_zero_true_gamma(monkeypatch): np.zeros((qcd + 1, qed + 1, 4, 4), dtype=complex), 2, 1, - 1, + [1,1], nf, ev_op_iterations, ev_op_max_order, diff --git a/tests/eko/test_kernels_QEDvalence.py b/tests/eko/test_kernels_QEDvalence.py index 5f0e95368..e894d91f6 100644 --- a/tests/eko/test_kernels_QEDvalence.py +++ b/tests/eko/test_kernels_QEDvalence.py @@ -50,7 +50,7 @@ def test_zero(monkeypatch): gamma_v, 1, 1, - 1, + [1,1], nf, ev_op_iterations, ev_op_max_order, @@ -64,7 +64,7 @@ def test_zero(monkeypatch): np.zeros((qcd + 1, qed + 1, 2, 2), dtype=complex), 2, 1, - 1, + [1,1], nf, ev_op_iterations, ev_op_max_order, @@ -102,7 +102,7 @@ def test_zero_true_gamma(monkeypatch): gamma_v, 1, 1, - 1, + [1,1], nf, ev_op_iterations, ev_op_max_order, @@ -116,7 +116,7 @@ def test_zero_true_gamma(monkeypatch): np.zeros((qcd + 1, qed + 1, 2, 2), dtype=complex), 2, 1, - 1, + [1,1], nf, ev_op_iterations, ev_op_max_order, From 9162e287998faed6fabd306b531bd014cf096e13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 3 Oct 2022 15:39:45 +0200 Subject: [PATCH 174/312] Modify way aem_liist is passed --- src/eko/evolution_operator/__init__.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index cc5e2a630..1dcd99d30 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -466,8 +466,13 @@ def a_s(self): a1 = sc.a_s(self.mur2_shift(self.q2_to), fact_scale=self.q2_to, nf_to=self.nf) return (a0, a1) - def aem_list_as(self, a0, a1): + @property + def aem_list_as(self): """Return the list of the couplings for the different values of :math:`a_s`.""" + if self.config["order"][1] == 0: + return [] + a0 = self.a_s[0] + a1 = self.a_s[1] sc = self.managers["couplings"] ev_op_iterations = self.config["ev_op_iterations"] as_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) @@ -523,7 +528,7 @@ def labels(self): labels.extend(br.singlet_unified_labels) return labels - def quad_ker(self, label, logx, areas, aem_list): + def quad_ker(self, label, logx, areas): """Return partially initialized integrand function. Parameters @@ -552,7 +557,7 @@ def quad_ker(self, label, logx, areas, aem_list): areas=areas, as1=self.a_s[1], as0=self.a_s[0], - aem_list=aem_list, + aem_list=self.aem_list_as, nf=self.nf, L=np.log(self.fact_to_ren), ev_op_iterations=self.config["ev_op_iterations"], @@ -602,8 +607,6 @@ def run_op_integration( column = [] k, logx = log_grid start_time = time.perf_counter() - # import pdb; pdb.set_trace() - aem_list = self.aem_list_as(self.a_s[0], self.a_s[1]) # iterate basis functions for l, bf in enumerate(self.int_disp): if k == l and l == self.grid_size - 1: @@ -612,7 +615,9 @@ def run_op_integration( # iterate sectors for label in self.labels: res = integrate.quad( - self.quad_ker(label, logx, bf.areas_representation, aem_list), + self.quad_ker( + label=label, logx=logx, areas=bf.areas_representation + ), 0.5, 1.0 - self._mellin_cut, epsabs=1e-12, From 774b39c71093773067b20a2b81f840ef571dd110 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 3 Oct 2022 15:46:22 +0200 Subject: [PATCH 175/312] Make aem_list np.array --- src/eko/evolution_operator/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 1dcd99d30..8411ce534 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -470,7 +470,7 @@ def a_s(self): def aem_list_as(self): """Return the list of the couplings for the different values of :math:`a_s`.""" if self.config["order"][1] == 0: - return [] + return np.array([]) a0 = self.a_s[0] a1 = self.a_s[1] sc = self.managers["couplings"] @@ -482,7 +482,7 @@ def aem_list_as(self): as_half = (as_h + as_l) / 2.0 aem_list.append(sc.compute_aem_as(as_to=as_half, nf=self.nf)) as_l = as_h - return aem_list + return np.array(aem_list) @property def labels(self): From 0a2d98dd879577f34c12ff09d9150bb98745ae66 Mon Sep 17 00:00:00 2001 From: niclaurenti Date: Mon, 3 Oct 2022 17:21:42 +0200 Subject: [PATCH 176/312] Fix parenthesis in compute_aem_as --- benchmarks/NNPDF_bench.py | 2 +- src/eko/couplings.py | 9 ++++----- src/eko/evolution_operator/__init__.py | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/benchmarks/NNPDF_bench.py b/benchmarks/NNPDF_bench.py index 72ada3a42..3793443eb 100644 --- a/benchmarks/NNPDF_bench.py +++ b/benchmarks/NNPDF_bench.py @@ -87,7 +87,7 @@ def benchmark_nnlo(self, Q0=1.65, Q2grid=(100,)): "Vu8", ] - operator_card = {**base_operator, "Q2grid": list(Q2grid)} + operator_card = {**base_operator, "Q2grid": list(Q2grid), "ev_op_iterations": 10} self.run([theory_card], [operator_card], ["NNPDF31_nnlo_as_0118_luxqed"]) diff --git a/src/eko/couplings.py b/src/eko/couplings.py index 7e90c4af1..2b12aa436 100644 --- a/src/eko/couplings.py +++ b/src/eko/couplings.py @@ -736,13 +736,12 @@ def compute_aem_as(self, as_to, nf): beta_qed_vec.append(beta_qed((0, 3), nf)) def rge(_as, a_em, beta_qcd_vec, beta_qcd_mix, beta_qed_vec, beta_qed_mix): - rge_qed = -( - a_em**2 - * (np.sum([a_em**k * b for k, b in enumerate(beta_qed_vec)])) + rge_qed = - a_em**2 * ( + np.sum([a_em**k * b for k, b in enumerate(beta_qed_vec)]) + beta_qed_mix * _as ) - rge_qcd = -( - _as**2 * (np.sum([_as**k * b for k, b in enumerate(beta_qcd_vec)])) + rge_qcd = - _as**2 * ( + np.sum([_as**k * b for k, b in enumerate(beta_qcd_vec)]) + beta_qcd_mix * a_em ) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 8411ce534..d262d5d47 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -469,7 +469,7 @@ def a_s(self): @property def aem_list_as(self): """Return the list of the couplings for the different values of :math:`a_s`.""" - if self.config["order"][1] == 0: + if self.order[1] == 0: return np.array([]) a0 = self.a_s[0] a1 = self.a_s[1] From 45a32984a7ec7fb851af7044725d286454046a95 Mon Sep 17 00:00:00 2001 From: niclaurenti Date: Mon, 3 Oct 2022 20:54:02 +0200 Subject: [PATCH 177/312] Implement function a_em in couplings --- src/eko/couplings.py | 17 +++++++++++++++++ src/eko/evolution_operator/__init__.py | 13 +++++++++++++ 2 files changed, 30 insertions(+) diff --git a/src/eko/couplings.py b/src/eko/couplings.py index 2b12aa436..a869724ab 100644 --- a/src/eko/couplings.py +++ b/src/eko/couplings.py @@ -877,6 +877,23 @@ def a_s(self, scale_to, fact_scale=None, nf_to=None): couplings :math:`a_s(\mu_R^2) = \frac{\alpha_s(\mu_R^2)}{4\pi}` """ return self.a(scale_to, fact_scale, nf_to)[0] + + def a_em(self, scale_to, fact_scale=None, nf_to=None): + r"""Compute coupling :math:`a_em(\mu_R^2) = \frac{\alpha_em(\mu_R^2)}{4\pi}`. + + Parameters + ---------- + scale_to : float + final scale to evolve to :math:`\mu_R^2` + fact_scale : float + factorization scale (if different from final scale) + + Returns + ------- + a_em : float + couplings :math:`a_em(\mu_R^2) = \frac{\alpha_em(\mu_R^2)}{4\pi}` + """ + return self.a(scale_to, fact_scale, nf_to)[1] def compute_matching_coeffs_up(mass_scheme, nf): diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index d262d5d47..a6ecaf07b 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -465,6 +465,16 @@ def a_s(self): ) a1 = sc.a_s(self.mur2_shift(self.q2_to), fact_scale=self.q2_to, nf_to=self.nf) return (a0, a1) + + @property + def a_em(self): + """Return the computed values for :math:`a_{em}`.""" + emc = self.managers["couplings"] + a0 = emc.a_em( + self.mur2_shift(self.q2_from), fact_scale=self.q2_from, nf_to=self.nf + ) + a1 = emc.a_em(self.mur2_shift(self.q2_to), fact_scale=self.q2_to, nf_to=self.nf) + return (a0, a1) @property def aem_list_as(self): @@ -677,6 +687,9 @@ def compute(self): logger.info( "%s: a_s distance: %e -> %e", self.log_label, self.a_s[0], self.a_s[1] ) + logger.info( + "%s: a_em distance: %e -> %e", self.log_label, self.a_em[0], self.a_em[1] + ) logger.info( "%s: order: (%d, %d), solution strategy: %s", self.log_label, From 55e80fe264680947e785d751ae700b05a9d014f8 Mon Sep 17 00:00:00 2001 From: niclaurenti Date: Tue, 4 Oct 2022 10:13:16 +0200 Subject: [PATCH 178/312] Implement running alpha in non singlet qed --- src/eko/kernels/QEDnon_singlet.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/eko/kernels/QEDnon_singlet.py b/src/eko/kernels/QEDnon_singlet.py index aa45fa030..e1d9aa08c 100644 --- a/src/eko/kernels/QEDnon_singlet.py +++ b/src/eko/kernels/QEDnon_singlet.py @@ -189,6 +189,18 @@ def nnlo_aem2_exact(gamma_ns, a1, a0, aem, nf): * ei.jm12_exact(a1, a0, aem, nf) ) +@nb.njit(cache=True) +def solution_running_alpha(func, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations): + """ + ... + """ + a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) + al = a_steps[0] + res = np.prod( + [ func(gamma_ns, ah, a_steps[step], aem_list[step], nf) for step, ah in enumerate(a_steps[1:])] + ) + return res + @nb.njit(cache=True) def dispatcher( @@ -224,7 +236,6 @@ def dispatcher( non-singlet EKO """ # use always exact in LO - aem = aem_list[0] if order[1] == 0: return non_singlet.dispatcher( order, method, gamma_ns[1:, 0], a1, a0, nf, ev_op_iterations @@ -233,16 +244,16 @@ def dispatcher( # the code never enters in this module if order[1] == 1: if order[0] == 1: - return lo_aem1_exact(gamma_ns, a1, a0, aem, nf) + return solution_running_alpha(lo_aem1_exact, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations) if order[0] == 2: - return nlo_aem1_exact(gamma_ns, a1, a0, aem, nf) + return solution_running_alpha(nlo_aem1_exact, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations) if order[0] == 3: - return nnlo_aem1_exact(gamma_ns, a1, a0, aem, nf) + return solution_running_alpha(nnlo_aem1_exact, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations) if order[1] == 2: if order[0] == 1: - return lo_aem2_exact(gamma_ns, a1, a0, aem, nf) + return solution_running_alpha(lo_aem2_exact, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations) if order[0] == 2: - return nlo_aem2_exact(gamma_ns, a1, a0, aem, nf) + return solution_running_alpha(nlo_aem2_exact, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations) if order[0] == 3: - return nnlo_aem2_exact(gamma_ns, a1, a0, aem, nf) + return solution_running_alpha(nnlo_aem2_exact, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations) raise NotImplementedError("Selected order is not implemented") From 0498d53543460a557cf18e81373baf2b09cc237f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 4 Oct 2022 12:11:44 +0200 Subject: [PATCH 179/312] Add nl to betaqed --- src/eko/beta.py | 34 ++++++++++--------- src/eko/couplings.py | 14 ++++---- src/eko/kernels/QEDnon_singlet.py | 56 ++++++++++++++++++++++++++----- 3 files changed, 73 insertions(+), 31 deletions(-) diff --git a/src/eko/beta.py b/src/eko/beta.py index 8532f0dd5..6326aa6a0 100644 --- a/src/eko/beta.py +++ b/src/eko/beta.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- r""" -This module contains the QCD beta function coefficients. +Compute the QCD beta function coefficients. See :doc:`pQCD ingredients `. """ @@ -13,7 +13,7 @@ @nb.njit(cache=True) def beta_qcd_as2(nf): - """Computes the first coefficient of the QCD beta function. + r"""Compute the first coefficient of the QCD beta function. Implements Eq. (3.1) of :cite:`Herzog:2017ohr`. @@ -34,7 +34,7 @@ def beta_qcd_as2(nf): @nb.njit(cache=True) def beta_qed_aem2(nf): - """Computes the first coefficient of the QED beta function. + r"""Compute the first coefficient of the QED beta function. Implements Eq. (7) of :cite:`Surguladze:1996hx`. @@ -51,13 +51,16 @@ def beta_qed_aem2(nf): """ nu = constants.uplike_flavors(nf) nd = nf - nu - beta_qed_aem2 = -4.0 / 3 * constants.NC * (nu * constants.eu2 + nd * constants.ed2) + nl = 3 # TODO : pass nf as an argument?? + beta_qed_aem2 = ( + -4.0 / 3 * constants.NC * (nl + nu * constants.eu2 + nd * constants.ed2) + ) return beta_qed_aem2 @nb.njit(cache=True) def beta_qcd_as3(nf): - """Computes the second coefficient of the QCD beta function. + r"""Compute the second coefficient of the QCD beta function. Implements Eq. (3.2) of :cite:`Herzog:2017ohr`. @@ -82,7 +85,7 @@ def beta_qcd_as3(nf): @nb.njit(cache=True) def beta_qed_aem3(nf): - """Computes the second coefficient of the QED beta function. + r"""Compute the second coefficient of the QED beta function. Implements Eq. (7) of :cite:`Surguladze:1996hx`. @@ -99,15 +102,16 @@ def beta_qed_aem3(nf): """ nu = constants.uplike_flavors(nf) nd = nf - nu + nl = 3 # TODO : pass nf as an argument?? beta_qed_aem3 = ( - -4.0 * constants.NC * (nu * constants.eu2**2 + nd * constants.ed2**2) + -4.0 * constants.NC * (nl + nu * constants.eu2**2 + nd * constants.ed2**2) ) return beta_qed_aem3 @nb.njit(cache=True) def beta_qcd_as2aem1(nf): - """Computes the first QED correction of the QCD beta function. + r"""Compute the first QED correction of the QCD beta function. Implements Eq. (7) of :cite:`Surguladze:1996hx`. @@ -130,7 +134,7 @@ def beta_qcd_as2aem1(nf): @nb.njit(cache=True) def beta_qed_aem2as1(nf): - """Computes the first QCD correction of the QED beta function. + r"""Compute the first QCD correction of the QED beta function. Implements Eq. (7) of :cite:`Surguladze:1996hx`. @@ -155,7 +159,7 @@ def beta_qed_aem2as1(nf): @nb.njit(cache=True) def beta_qcd_as4(nf): - """Computes the third coefficient of the QCD beta function + r"""Compute the third coefficient of the QCD beta function. Implements Eq. (3.3) of :cite:`Herzog:2017ohr`. @@ -184,7 +188,7 @@ def beta_qcd_as4(nf): @nb.njit(cache=True) def beta_qcd_as5(nf): - """Computes the fourth coefficient of the QCD beta function + r"""Compute the fourth coefficient of the QCD beta function. Implements Eq. (3.6) of :cite:`Herzog:2017ohr`. @@ -211,7 +215,7 @@ def beta_qcd_as5(nf): @nb.njit(cache=True) def beta_qcd(k, nf): - """Compute value of a beta_qcd coefficients + r"""Compute value of a beta_qcd coefficients. Parameters ---------- @@ -244,7 +248,7 @@ def beta_qcd(k, nf): @nb.njit(cache=True) def beta_qed(k, nf): - """Compute value of a beta_qed coefficients + r"""Compute value of a beta_qed coefficients. Parameters ---------- @@ -273,7 +277,7 @@ def beta_qed(k, nf): @nb.njit(cache=True) def b_qcd(k, nf): - """Compute b_qcd coefficient. + r"""Compute b_qcd coefficient. Parameters ---------- @@ -293,7 +297,7 @@ def b_qcd(k, nf): @nb.njit(cache=True) def b_qed(k, nf): - """Compute b_qed coefficient. + r"""Compute b_qed coefficient. Parameters ---------- diff --git a/src/eko/couplings.py b/src/eko/couplings.py index a869724ab..d157c7589 100644 --- a/src/eko/couplings.py +++ b/src/eko/couplings.py @@ -622,8 +622,8 @@ def compute_exact_running_alphaem(self, a_ref, nf, scale_from, scale_to): beta_qed_vec = [beta_qed((0, 2), nf)] beta_qcd_mix = beta_qcd((2, 1), nf) beta_qed_mix = beta_qed((1, 2), nf) # order[0] is always at least 1 - if self.order[1] >= 2: - beta_qed_vec.append(beta_qed((0, 3), nf)) + if self.order[1] >= 2: + beta_qed_vec.append(beta_qed((0, 3), nf)) # integration kernel def rge(_t, a, beta_qcd_vec, beta_qcd_mix, beta_qed_vec, beta_qed_mix): rge_qcd = -(a[0] ** 2) * ( @@ -732,15 +732,15 @@ def compute_aem_as(self, as_to, nf): beta_qed_vec = [beta_qed((0, 2), nf)] beta_qcd_mix = beta_qcd((2, 1), nf) beta_qed_mix = beta_qed((1, 2), nf) # order[0] is always at least 1 - if self.order[1] >= 2: - beta_qed_vec.append(beta_qed((0, 3), nf)) + if self.order[1] >= 2: + beta_qed_vec.append(beta_qed((0, 3), nf)) def rge(_as, a_em, beta_qcd_vec, beta_qcd_mix, beta_qed_vec, beta_qed_mix): - rge_qed = - a_em**2 * ( + rge_qed = -(a_em**2) * ( np.sum([a_em**k * b for k, b in enumerate(beta_qed_vec)]) + beta_qed_mix * _as ) - rge_qcd = - _as**2 * ( + rge_qcd = -(_as**2) * ( np.sum([_as**k * b for k, b in enumerate(beta_qcd_vec)]) + beta_qcd_mix * a_em ) @@ -877,7 +877,7 @@ def a_s(self, scale_to, fact_scale=None, nf_to=None): couplings :math:`a_s(\mu_R^2) = \frac{\alpha_s(\mu_R^2)}{4\pi}` """ return self.a(scale_to, fact_scale, nf_to)[0] - + def a_em(self, scale_to, fact_scale=None, nf_to=None): r"""Compute coupling :math:`a_em(\mu_R^2) = \frac{\alpha_em(\mu_R^2)}{4\pi}`. diff --git a/src/eko/kernels/QEDnon_singlet.py b/src/eko/kernels/QEDnon_singlet.py index e1d9aa08c..23521c87d 100644 --- a/src/eko/kernels/QEDnon_singlet.py +++ b/src/eko/kernels/QEDnon_singlet.py @@ -189,15 +189,41 @@ def nnlo_aem2_exact(gamma_ns, a1, a0, aem, nf): * ei.jm12_exact(a1, a0, aem, nf) ) + @nb.njit(cache=True) def solution_running_alpha(func, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations): """ - ... + Compute non-singlet EKO with running alphaem. + + Parameters + ---------- + func: function + solution for fixed alphaem + gamma_ns : numpy.ndarray + non-singlet anomalous dimensions + a1 : float + target coupling value + a0 : float + initial coupling value + aem_list : numpy.ndarray + electromagnetic coupling values + nf : int + number of active flavors + ev_op_iterations : int + number of evolution steps + + Returns + ------- + e_ns : complex + non-singlet EKO + """ a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) - al = a_steps[0] res = np.prod( - [ func(gamma_ns, ah, a_steps[step], aem_list[step], nf) for step, ah in enumerate(a_steps[1:])] + [ + func(gamma_ns, ah, a_steps[step], aem_list[step], nf) + for step, ah in enumerate(a_steps[1:]) + ] ) return res @@ -244,16 +270,28 @@ def dispatcher( # the code never enters in this module if order[1] == 1: if order[0] == 1: - return solution_running_alpha(lo_aem1_exact, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations) + return solution_running_alpha( + lo_aem1_exact, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations + ) if order[0] == 2: - return solution_running_alpha(nlo_aem1_exact, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations) + return solution_running_alpha( + nlo_aem1_exact, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations + ) if order[0] == 3: - return solution_running_alpha(nnlo_aem1_exact, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations) + return solution_running_alpha( + nnlo_aem1_exact, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations + ) if order[1] == 2: if order[0] == 1: - return solution_running_alpha(lo_aem2_exact, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations) + return solution_running_alpha( + lo_aem2_exact, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations + ) if order[0] == 2: - return solution_running_alpha(nlo_aem2_exact, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations) + return solution_running_alpha( + nlo_aem2_exact, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations + ) if order[0] == 3: - return solution_running_alpha(nnlo_aem2_exact, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations) + return solution_running_alpha( + nnlo_aem2_exact, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations + ) raise NotImplementedError("Selected order is not implemented") From bd11a38aff5f5f9266d7007742f17e06f3967877 Mon Sep 17 00:00:00 2001 From: niclaurenti Date: Tue, 4 Oct 2022 12:55:07 +0200 Subject: [PATCH 180/312] Add Qedref to theory card --- benchmarks/NNPDF_bench.py | 3 ++- benchmarks/apfel_bench.py | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/benchmarks/NNPDF_bench.py b/benchmarks/NNPDF_bench.py index 3793443eb..5ee21c7c8 100644 --- a/benchmarks/NNPDF_bench.py +++ b/benchmarks/NNPDF_bench.py @@ -35,6 +35,7 @@ def skip_pdfs(self, _theory): base_theory = { "Qref": 91.2, + "Qedref": 91.2, "mc": 1.51, "mb": 4.92, "mt": 172.5, @@ -128,7 +129,7 @@ def benchmark_nnlo(self, Q0=1.65, Q2grid=(100,)): # # test backward # #nn31.benchmark_nlo(Q0=np.sqrt(high2), Q2grid=[low2]) nn31qed = BenchmarkNNPDF31_luxqed() - nn31qed.benchmark_nnlo() + nn31qed.benchmark_nnlo(Q0=5., Q2grid=(10000,)) # nn40 = BenchmarkNNPDF40() # nn40.benchmark_nnlo(Q2grid=[100]) # nn40.benchmark_nnlo(Q0=np.sqrt(high2), Q2grid=[low2]) diff --git a/benchmarks/apfel_bench.py b/benchmarks/apfel_bench.py index 9b6a1d646..d4982c684 100644 --- a/benchmarks/apfel_bench.py +++ b/benchmarks/apfel_bench.py @@ -193,6 +193,7 @@ class BenchmarkFFNS_qed(ApfelBenchmark): ffns_theory = { "Qref": 91.1870, + "Qedref": 91.1870, "mc": 1.3, "mb": 4.75, "mt": 172.0, @@ -235,6 +236,7 @@ class BenchmarkVFNS_qed(ApfelBenchmark): vfns_theory = { "Qref": 91.1870, + "Qedref": 91.1870, "mc": 1.3, "mb": 4.75, "mt": 172.0, @@ -282,5 +284,5 @@ def benchmark_plain(self, pto, qed): # obj.benchmark_msbar(2) # obj = BenchmarkFFNS_qed() - obj = BenchmarkVFNS_qed() + obj = BenchmarkFFNS_qed() obj.benchmark_plain(2, 2) From fb6459185ac4c283ee76a9b8f8cd5482719fbaab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 4 Oct 2022 13:48:08 +0200 Subject: [PATCH 181/312] Fix test_beta.py --- tests/eko/test_beta.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/eko/test_beta.py b/tests/eko/test_beta.py index f3c467e47..ff6f5f714 100644 --- a/tests/eko/test_beta.py +++ b/tests/eko/test_beta.py @@ -29,7 +29,7 @@ def test_beta_aem2(): """Test first beta function coefficient""" # from hep-ph/9706430 np.testing.assert_approx_equal( - beta.beta_qed_aem2(5), -4.0 / 3 * 3 * (2 * 4 / 9 + 3 * 1 / 9) + beta.beta_qed_aem2(5), -4.0 / 3 * 3 * (3 + 2 * 4 / 9 + 3 * 1 / 9) ) @@ -44,7 +44,7 @@ def test_beta_aem3(): """Test second beta function coefficient""" # from hep-ph/9706430 np.testing.assert_approx_equal( - beta.beta_qed_aem3(5), -4.0 * 3 * (2 * 16 / 81 + 3 * 1 / 81) + beta.beta_qed_aem3(5), -4.0 * 3 * (3 + 2 * 16 / 81 + 3 * 1 / 81) ) From b7037cc759eefc36769d2a31d7f30fc0b0c2f8a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 4 Oct 2022 14:08:01 +0200 Subject: [PATCH 182/312] Revert "Add Qedref to theory card" This reverts commit bd11a38aff5f5f9266d7007742f17e06f3967877. --- benchmarks/NNPDF_bench.py | 3 +-- benchmarks/apfel_bench.py | 4 +--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/benchmarks/NNPDF_bench.py b/benchmarks/NNPDF_bench.py index 5ee21c7c8..3793443eb 100644 --- a/benchmarks/NNPDF_bench.py +++ b/benchmarks/NNPDF_bench.py @@ -35,7 +35,6 @@ def skip_pdfs(self, _theory): base_theory = { "Qref": 91.2, - "Qedref": 91.2, "mc": 1.51, "mb": 4.92, "mt": 172.5, @@ -129,7 +128,7 @@ def benchmark_nnlo(self, Q0=1.65, Q2grid=(100,)): # # test backward # #nn31.benchmark_nlo(Q0=np.sqrt(high2), Q2grid=[low2]) nn31qed = BenchmarkNNPDF31_luxqed() - nn31qed.benchmark_nnlo(Q0=5., Q2grid=(10000,)) + nn31qed.benchmark_nnlo() # nn40 = BenchmarkNNPDF40() # nn40.benchmark_nnlo(Q2grid=[100]) # nn40.benchmark_nnlo(Q0=np.sqrt(high2), Q2grid=[low2]) diff --git a/benchmarks/apfel_bench.py b/benchmarks/apfel_bench.py index d4982c684..9b6a1d646 100644 --- a/benchmarks/apfel_bench.py +++ b/benchmarks/apfel_bench.py @@ -193,7 +193,6 @@ class BenchmarkFFNS_qed(ApfelBenchmark): ffns_theory = { "Qref": 91.1870, - "Qedref": 91.1870, "mc": 1.3, "mb": 4.75, "mt": 172.0, @@ -236,7 +235,6 @@ class BenchmarkVFNS_qed(ApfelBenchmark): vfns_theory = { "Qref": 91.1870, - "Qedref": 91.1870, "mc": 1.3, "mb": 4.75, "mt": 172.0, @@ -284,5 +282,5 @@ def benchmark_plain(self, pto, qed): # obj.benchmark_msbar(2) # obj = BenchmarkFFNS_qed() - obj = BenchmarkFFNS_qed() + obj = BenchmarkVFNS_qed() obj.benchmark_plain(2, 2) From f7ff8e4424c887d766a3573e2c3a04021d1e3b15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 4 Oct 2022 15:27:59 +0200 Subject: [PATCH 183/312] Pass alphaem_running from dict --- src/eko/compatibility.py | 4 +++ src/eko/couplings.py | 29 ++++++++++--------- tests/eko/test_couplings.py | 56 +++++++++++++++++++------------------ 3 files changed, 49 insertions(+), 40 deletions(-) diff --git a/src/eko/compatibility.py b/src/eko/compatibility.py index a41ce4d91..8148c1886 100644 --- a/src/eko/compatibility.py +++ b/src/eko/compatibility.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +"""Transform the theory_card in order to be compatible with EKO.""" import copy @@ -26,6 +27,9 @@ def update(theory, operators): new_theory["alphaem"] = new_theory.pop("alphaqed") if "QED" in new_theory: new_theory["order"] = (new_theory.pop("PTO") + 1, new_theory.pop("QED")) + if "alphaem_running" not in new_theory: + new_theory["alphaem_running"] = False + # TODO : add alphaem_running to the runcard if operators is not None: if isinstance(new_operators["ev_op_max_order"], int): new_operators["ev_op_max_order"] = ( diff --git a/src/eko/couplings.py b/src/eko/couplings.py index d157c7589..27eb92c06 100644 --- a/src/eko/couplings.py +++ b/src/eko/couplings.py @@ -265,7 +265,7 @@ def expanded_qed(ref, order, nf, lmu): @nb.njit(cache=True) -def couplings_expanded_running_alphaem(order, couplings_ref, nf, scale_from, scale_to): +def couplings_expanded_alphaem_running(order, couplings_ref, nf, scale_from, scale_to): r"""Compute coupled expanded expression of the couplings for running alphaem. Implement Eqs. (17-18) from :cite:`Surguladze:1996hx` @@ -421,7 +421,7 @@ def __init__( nf_ref=None, max_nf=None, hqm_scheme="POLE", - running_alphaem=False, + alphaem_running=False, ): # Sanity checks if couplings_ref[0] <= 0: @@ -442,9 +442,8 @@ def __init__( if method not in ["expanded", "exact"]: raise ValueError(f"Unknown method {method}") self.method = method - # self.running_alphaem = running_alphaem - self.running_alphaem = True - # running_alphaem is put for a future implementation of the DGLAP solution + self.alphaem_running = alphaem_running + # alphaem_running is put for a future implementation of the DGLAP solution # with running alpha. For the moment in must remain set to False # create new threshold object @@ -477,7 +476,7 @@ def q2_ref(self): return self.thresholds.q2_ref @classmethod - def from_dict(cls, theory_card, masses=None, running_alphaem=False): + def from_dict(cls, theory_card, masses=None): r"""Create object from theory dictionary. Parameters @@ -517,6 +516,10 @@ def from_dict(cls, theory_card, masses=None, running_alphaem=False): [theory_card[f"k{q}Thr"] for q in heavy_flavors], 2 ) max_nf = theory_card["MaxNfAs"] + if order[1] > 0: + alphaem_running = theory_card["alphaem_running"] + else: + alphaem_running = False return cls( couplings_ref, q2_alpha, @@ -527,7 +530,7 @@ def from_dict(cls, theory_card, masses=None, running_alphaem=False): nf_ref, max_nf, hqm_scheme, - running_alphaem, + alphaem_running, ) def unidimensional_exact(self, beta0, b_vec, u, a_ref, method, rtol): @@ -564,7 +567,7 @@ def rge(_t, a, b_vec): ) return res.y[0][-1] - def compute_exact_running_alphaem(self, a_ref, nf, scale_from, scale_to): + def compute_exact_alphaem_running(self, a_ref, nf, scale_from, scale_to): """Compute couplings via |RGE| with running alphaem. Parameters @@ -713,7 +716,7 @@ def compute_aem_as(self, as_to, nf): float a_em at target a_s :math:`a_em(a_s)` """ - if not self.running_alphaem: + if not self.alphaem_running: return self.a_ref[1] beta_qcd_vec = [beta_qcd((2, 0), nf)] beta_qed_vec = [] @@ -785,8 +788,8 @@ def compute(self, a_ref, nf, scale_from, scale_to): except KeyError: # at the moment everything is expanded - and type has been checked in the constructor if self.method == "exact": - if self.running_alphaem: - a_new = self.compute_exact_running_alphaem( + if self.alphaem_running: + a_new = self.compute_exact_alphaem_running( a_ref.astype(float), nf, scale_from, scale_to ) else: @@ -794,8 +797,8 @@ def compute(self, a_ref, nf, scale_from, scale_to): a_ref.astype(float), nf, scale_from, scale_to ) else: - if self.running_alphaem: - a_new = couplings_expanded_running_alphaem( + if self.alphaem_running: + a_new = couplings_expanded_alphaem_running( self.order, a_ref.astype(float), nf, scale_from, float(scale_to) ) else: diff --git a/tests/eko/test_couplings.py b/tests/eko/test_couplings.py index 0f681104d..a2643b500 100644 --- a/tests/eko/test_couplings.py +++ b/tests/eko/test_couplings.py @@ -30,6 +30,7 @@ def test_from_dict(self): "MaxNfAs": 6, "HQ": "POLE", "ModSV": None, + "alphaem_running": False, } sc = Couplings.from_dict(theory_dict) assert sc.a(theory_dict["Qref"] ** 2)[0] == theory_dict["alphas"] / ( @@ -78,6 +79,7 @@ def test_init(self): MaxNfAs=6, HQ="POLE", ModSV=None, + alphaem_running=False, ) sc2 = Couplings.from_dict(setup) assert sc2.q2_ref == scale_ref @@ -173,7 +175,7 @@ def test_ref(self): for order_s in [1, 2, 3, 4]: for order_em in [0, 1, 2]: for method in ["exact", "expanded"]: - for running_alphaem in [True, False]: + for alphaem_running in [True, False]: # if order_em == 1 and method == "expanded" and order_s != 0: # continue # create @@ -184,7 +186,7 @@ def test_ref(self): (1.0, 1.0, 1.0), (order_s, order_em), method, - running_alphaem=running_alphaem, + alphaem_running=alphaem_running, ) np.testing.assert_approx_equal( sc.a(scale_ref)[0], alphas_ref / 4.0 / np.pi @@ -231,7 +233,7 @@ def test_exact_LO(self): alphaem_ref = 0.00781 scale_ref = 91.0**2 for thresh_setup in thresh_setups: - for running_alphaem in [True, False]: + for alphaem_running in [True, False]: # in LO expanded = exact sc_expanded = Couplings( np.array([alphas_ref, alphaem_ref]), @@ -240,7 +242,7 @@ def test_exact_LO(self): (1.0, 1.0, 1.0), (1, 0), "expanded", - running_alphaem=running_alphaem, + alphaem_running=alphaem_running, ) sc_exact = Couplings( np.array([alphas_ref, alphaem_ref]), @@ -249,7 +251,7 @@ def test_exact_LO(self): (1.0, 1.0, 1.0), (1, 0), "exact", - running_alphaem=running_alphaem, + alphaem_running=alphaem_running, ) for q2 in [1, 1e1, 1e2, 1e3, 1e4]: np.testing.assert_allclose( @@ -270,7 +272,7 @@ def test_exact_NLO(self): alphaem_ref = 0.00781 scale_ref = 91.0**2 for thresh_setup in thresh_setups: - for running_alphaem in [True, False]: + for alphaem_running in [True, False]: # in LO expanded = exact sc_expanded = Couplings( np.array([alphas_ref, alphaem_ref]), @@ -279,7 +281,7 @@ def test_exact_NLO(self): (1.0, 1.0, 1.0), (2, 0), "expanded", - running_alphaem=running_alphaem, + alphaem_running=alphaem_running, ) sc_exact = Couplings( np.array([alphas_ref, alphaem_ref]), @@ -288,7 +290,7 @@ def test_exact_NLO(self): (1.0, 1.0, 1.0), (2, 0), "exact", - running_alphaem=running_alphaem, + alphaem_running=alphaem_running, ) for q2 in [1e2, 1e3, 1e4]: np.testing.assert_allclose( @@ -310,7 +312,7 @@ def test_exact_LO_QED(self): scale_ref = 91.0**2 for PTOs in range(1, 2 + 1): for thresh_setup in thresh_setups: - for running_alphaem in [True, False]: + for alphaem_running in [True, False]: # in LO expanded = exact sc_expanded = Couplings( np.array([alphas_ref, alphaem_ref]), @@ -319,7 +321,7 @@ def test_exact_LO_QED(self): (1.0, 1.0, 1.0), (PTOs, 1), "expanded", - running_alphaem=running_alphaem, + alphaem_running=alphaem_running, ) sc_exact = Couplings( np.array([alphas_ref, alphaem_ref]), @@ -328,7 +330,7 @@ def test_exact_LO_QED(self): (1.0, 1.0, 1.0), (PTOs, 1), "exact", - running_alphaem=running_alphaem, + alphaem_running=alphaem_running, ) for q2 in [1e2, 1e3, 1e4]: np.testing.assert_allclose( @@ -350,7 +352,7 @@ def test_exact_NLO_QED(self): scale_ref = 91.0**2 for PTOs in range(1, 4 + 1): for thresh_setup in thresh_setups: - for running_alphaem in [True, False]: + for alphaem_running in [True, False]: # in LO expanded = exact sc_expanded = Couplings( np.array([alphas_ref, alphaem_ref]), @@ -359,7 +361,7 @@ def test_exact_NLO_QED(self): (1.0, 1.0, 1.0), (PTOs, 2), "expanded", - running_alphaem=running_alphaem, + alphaem_running=alphaem_running, ) sc_exact = Couplings( np.array([alphas_ref, alphaem_ref]), @@ -368,7 +370,7 @@ def test_exact_NLO_QED(self): (1.0, 1.0, 1.0), (PTOs, 2), "exact", - running_alphaem=running_alphaem, + alphaem_running=alphaem_running, ) for q2 in [1e2, 1e3, 1e4]: np.testing.assert_allclose( @@ -389,7 +391,7 @@ def test_exact_NLO_mix(self): alphaem_ref = 0.00781 scale_ref = 91.0**2 for thresh_setup in thresh_setups: - for running_alphaem in [True, False]: + for alphaem_running in [True, False]: # in LO expanded = exact sc_expanded = Couplings( np.array([alphas_ref, alphaem_ref]), @@ -398,7 +400,7 @@ def test_exact_NLO_mix(self): (1.0, 1.0, 1.0), (2, 2), "expanded", - running_alphaem=running_alphaem, + alphaem_running=alphaem_running, ) sc_exact = Couplings( np.array([alphas_ref, alphaem_ref]), @@ -407,7 +409,7 @@ def test_exact_NLO_mix(self): (1.0, 1.0, 1.0), (2, 2), "exact", - running_alphaem=running_alphaem, + alphaem_running=alphaem_running, ) for q2 in [1e2, 1e3, 1e4]: np.testing.assert_allclose( @@ -428,7 +430,7 @@ def test_exact_N2LO_mix(self): alphaem_ref = 0.00781 scale_ref = 91.0**2 for thresh_setup in thresh_setups: - for running_alphaem in [True, False]: + for alphaem_running in [True, False]: # in LO expanded = exact sc_expanded = Couplings( np.array([alphas_ref, alphaem_ref]), @@ -437,7 +439,7 @@ def test_exact_N2LO_mix(self): (1.0, 1.0, 1.0), (3, 2), "expanded", - running_alphaem=running_alphaem, + alphaem_running=alphaem_running, ) sc_exact = Couplings( np.array([alphas_ref, alphaem_ref]), @@ -446,7 +448,7 @@ def test_exact_N2LO_mix(self): (1.0, 1.0, 1.0), (3, 2), "exact", - running_alphaem=running_alphaem, + alphaem_running=alphaem_running, ) for q2 in [1e1, 1e2, 1e3, 1e4]: np.testing.assert_allclose( @@ -467,7 +469,7 @@ def test_exact_N3LO_mix(self): alphaem_ref = 0.00781 scale_ref = 91.0**2 for thresh_setup in thresh_setups: - for running_alphaem in [True, False]: + for alphaem_running in [True, False]: # in LO expanded = exact sc_expanded = Couplings( np.array([alphas_ref, alphaem_ref]), @@ -476,7 +478,7 @@ def test_exact_N3LO_mix(self): (1.0, 1.0, 1.0), (4, 2), "expanded", - running_alphaem=running_alphaem, + alphaem_running=alphaem_running, ) sc_exact = Couplings( np.array([alphas_ref, alphaem_ref]), @@ -485,7 +487,7 @@ def test_exact_N3LO_mix(self): (1.0, 1.0, 1.0), (4, 2), "exact", - running_alphaem=running_alphaem, + alphaem_running=alphaem_running, ) for q2 in [1e1, 1e2, 1e3, 1e4]: np.testing.assert_allclose( @@ -547,7 +549,7 @@ def test_running_alpha(self): (1.0, 1.0, 1.0), (qcd, 0), "expanded", - running_alphaem=True, + alphaem_running=True, ) sc_expanded_fixed = Couplings( np.array([alphas_ref, alphaem_ref]), @@ -556,7 +558,7 @@ def test_running_alpha(self): (1.0, 1.0, 1.0), (qcd, 0), "expanded", - running_alphaem=False, + alphaem_running=False, ) sc_exact_running = Couplings( np.array([alphas_ref, alphaem_ref]), @@ -565,7 +567,7 @@ def test_running_alpha(self): (1.0, 1.0, 1.0), (qcd, 0), "exact", - running_alphaem=True, + alphaem_running=True, ) sc_exact_fixed = Couplings( np.array([alphas_ref, alphaem_ref]), @@ -574,7 +576,7 @@ def test_running_alpha(self): (1.0, 1.0, 1.0), (qcd, 0), "exact", - running_alphaem=False, + alphaem_running=False, ) for q2 in [1e1, 1e2, 1e3, 1e4]: # for pure qcd running or fixed alpha must be equal From 6c5a73a2d602a380cc84c4114c43d0c37b0d12eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 4 Oct 2022 15:50:25 +0200 Subject: [PATCH 184/312] Test compute_aem_as --- tests/eko/test_couplings.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/eko/test_couplings.py b/tests/eko/test_couplings.py index a2643b500..466189c3d 100644 --- a/tests/eko/test_couplings.py +++ b/tests/eko/test_couplings.py @@ -586,3 +586,36 @@ def test_running_alpha(self): np.testing.assert_allclose( sc_exact_running.a(q2), sc_exact_fixed.a(q2), rtol=1e-10 ) + + def test_compute_aem_as(self): + alphas_ref = 0.118 + alphaem_ref = 0.00781 + scale_ref = 91.2**2 + thresh_setups = [ + # (np.inf, np.inf, np.inf), + # (0, np.inf, np.inf), + (2, 4, 175), + ] + scale_target = [5, 10, 50, 100] + for running_alphaem in [True, False]: + for thresh_setup in thresh_setups: + for qcd in range(1, 4 + 1): + for qed in range(0, 2 + 1): + couplings = Couplings( + np.array([alphas_ref, alphaem_ref]), + scale_ref, + thresh_setup, + (1.0, 1.0, 1.0), + (qcd, qed), + "exact", + alphaem_running=running_alphaem, + ) + for Qf in scale_target: + a_values = [] + a_values.append(couplings.a(Qf**2)) + aem_values = [] + for a in a_values: + aem = couplings.compute_aem_as(a[0], nf=5) + np.testing.assert_allclose( + aem, a[1], atol=1e-6, rtol=1e-2 + ) From 823d6dd2a3ae0025c0f45fdfde3ec7bc9374c108 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 4 Oct 2022 16:25:38 +0200 Subject: [PATCH 185/312] test aem_list --- tests/eko/test_ev_operator.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/eko/test_ev_operator.py b/tests/eko/test_ev_operator.py index fe32754b0..da338c246 100644 --- a/tests/eko/test_ev_operator.py +++ b/tests/eko/test_ev_operator.py @@ -15,6 +15,7 @@ from eko.kernels import QEDnon_singlet as qed_ns from eko.kernels import non_singlet as ns from eko.kernels import singlet as s +from eko.kernels import utils from eko.thresholds import ThresholdsAtlas @@ -421,6 +422,36 @@ def test_compute_parallel(self, monkeypatch): o.compute() self.check_lo(o) + def test_aem_list(self): + tcard = copy.deepcopy(theory_card) + ocard = copy.deepcopy(operators_card) + ocard["n_integration_cores"] = 2 + ocard["ev_op_iterations"] = 10 + for qcd in range(1, 3 + 1): + for qed in range(1, 2 + 1): + for aem_running in [True, False]: + tcard["order"] = (qcd, qed) + tcard["alphaem_running"] = aem_running + g = OperatorGrid.from_dict( + tcard, + ocard, + ThresholdsAtlas.from_dict(tcard), + Couplings.from_dict(tcard), + InterpolatorDispatcher.from_dict(ocard), + ) + o = Operator(g.config, g.managers, 3, 2.0, 2.0) + couplings = Couplings.from_dict(tcard) + aem_list = o.aem_list_as + (a0, a1) = o.a_s + ev_op_iterations = ocard["ev_op_iterations"] + as_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) + as_l = as_steps[0] + for step, as_h in enumerate(as_steps[1:]): + as_half = (as_h + as_l) / 2.0 + aem = couplings.compute_aem_as(as_half, 3) + np.testing.assert_allclose(aem, aem_list[step]) + as_l = as_h + def check_lo(self, o): assert (br.non_singlet_pids_map["ns-"], 0) in o.op_members np.testing.assert_allclose( From aea61b2c26b8c8692680079b796d5c255e26ecf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 4 Oct 2022 17:32:49 +0200 Subject: [PATCH 186/312] Fix reference in test_beta --- tests/eko/test_beta.py | 4 ++-- tests/eko/test_couplings.py | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/eko/test_beta.py b/tests/eko/test_beta.py index ff6f5f714..95f804d06 100644 --- a/tests/eko/test_beta.py +++ b/tests/eko/test_beta.py @@ -27,7 +27,7 @@ def test_beta_as2(): def test_beta_aem2(): """Test first beta function coefficient""" - # from hep-ph/9706430 + # from hep-ph/9803211 np.testing.assert_approx_equal( beta.beta_qed_aem2(5), -4.0 / 3 * 3 * (3 + 2 * 4 / 9 + 3 * 1 / 9) ) @@ -42,7 +42,7 @@ def test_beta_as3(): def test_beta_aem3(): """Test second beta function coefficient""" - # from hep-ph/9706430 + # from hep-ph/9803211 np.testing.assert_approx_equal( beta.beta_qed_aem3(5), -4.0 * 3 * (3 + 2 * 16 / 81 + 3 * 1 / 81) ) diff --git a/tests/eko/test_couplings.py b/tests/eko/test_couplings.py index 466189c3d..9939816a0 100644 --- a/tests/eko/test_couplings.py +++ b/tests/eko/test_couplings.py @@ -596,7 +596,7 @@ def test_compute_aem_as(self): # (0, np.inf, np.inf), (2, 4, 175), ] - scale_target = [5, 10, 50, 100] + scale_target = [1, 5, 10, 50, 100, 500] for running_alphaem in [True, False]: for thresh_setup in thresh_setups: for qcd in range(1, 4 + 1): @@ -613,7 +613,6 @@ def test_compute_aem_as(self): for Qf in scale_target: a_values = [] a_values.append(couplings.a(Qf**2)) - aem_values = [] for a in a_values: aem = couplings.compute_aem_as(a[0], nf=5) np.testing.assert_allclose( From 74062ffffd286ff9899d00eb06d102fd8d26fd79 Mon Sep 17 00:00:00 2001 From: niclaurenti Date: Wed, 5 Oct 2022 10:43:16 +0200 Subject: [PATCH 187/312] Make numba compile ns qed sector --- src/eko/kernels/QEDnon_singlet.py | 115 ++++++++++++++++-------------- 1 file changed, 61 insertions(+), 54 deletions(-) diff --git a/src/eko/kernels/QEDnon_singlet.py b/src/eko/kernels/QEDnon_singlet.py index 23521c87d..26b998ba4 100644 --- a/src/eko/kernels/QEDnon_singlet.py +++ b/src/eko/kernels/QEDnon_singlet.py @@ -190,42 +190,45 @@ def nnlo_aem2_exact(gamma_ns, a1, a0, aem, nf): ) -@nb.njit(cache=True) -def solution_running_alpha(func, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations): - """ - Compute non-singlet EKO with running alphaem. - - Parameters - ---------- - func: function - solution for fixed alphaem - gamma_ns : numpy.ndarray - non-singlet anomalous dimensions - a1 : float - target coupling value - a0 : float - initial coupling value - aem_list : numpy.ndarray - electromagnetic coupling values - nf : int - number of active flavors - ev_op_iterations : int - number of evolution steps - - Returns - ------- - e_ns : complex - non-singlet EKO - - """ - a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) - res = np.prod( - [ - func(gamma_ns, ah, a_steps[step], aem_list[step], nf) - for step, ah in enumerate(a_steps[1:]) - ] - ) - return res +# @nb.njit(cache=True) +# def solution_running_alpha(func, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations): +# """ +# Compute non-singlet EKO with running alphaem. + +# Parameters +# ---------- +# func: function +# solution for fixed alphaem +# gamma_ns : numpy.ndarray +# non-singlet anomalous dimensions +# a1 : float +# target coupling value +# a0 : float +# initial coupling value +# aem_list : numpy.ndarray +# electromagnetic coupling values +# nf : int +# number of active flavors +# ev_op_iterations : int +# number of evolution steps + +# Returns +# ------- +# e_ns : complex +# non-singlet EKO + +# """ +# a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) +# # res = np.prod( +# # [ +# # func(gamma_ns, ah, a_steps[step], aem_list[step], nf) +# # for step, ah in enumerate(a_steps[1:]) +# # ] +# # ) +# res = 1. +# for step, ah in enumerate(a_steps[1:]): +# res *= func(gamma_ns, ah, a_steps[step], aem_list[step], nf) +# return res @nb.njit(cache=True) @@ -268,30 +271,34 @@ def dispatcher( ) # this if is probably useless since when order[1] == 0 # the code never enters in this module + a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) + res = 1. + # For the moment implemented in this way to make numba compile it + # TODO : implement it with np.prod in a way that numba compiles it if order[1] == 1: if order[0] == 1: - return solution_running_alpha( - lo_aem1_exact, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations - ) + for step, ah in enumerate(a_steps[1:]): + res *= lo_aem1_exact(gamma_ns, ah, a_steps[step], aem_list[step], nf) + return res if order[0] == 2: - return solution_running_alpha( - nlo_aem1_exact, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations - ) + for step, ah in enumerate(a_steps[1:]): + res *= nlo_aem1_exact(gamma_ns, ah, a_steps[step], aem_list[step], nf) + return res if order[0] == 3: - return solution_running_alpha( - nnlo_aem1_exact, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations - ) + for step, ah in enumerate(a_steps[1:]): + res *= nnlo_aem1_exact(gamma_ns, ah, a_steps[step], aem_list[step], nf) + return res if order[1] == 2: if order[0] == 1: - return solution_running_alpha( - lo_aem2_exact, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations - ) + for step, ah in enumerate(a_steps[1:]): + res *= lo_aem2_exact(gamma_ns, ah, a_steps[step], aem_list[step], nf) + return res if order[0] == 2: - return solution_running_alpha( - nlo_aem2_exact, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations - ) + for step, ah in enumerate(a_steps[1:]): + res *= nlo_aem2_exact(gamma_ns, ah, a_steps[step], aem_list[step], nf) + return res if order[0] == 3: - return solution_running_alpha( - nnlo_aem2_exact, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations - ) + for step, ah in enumerate(a_steps[1:]): + res *= nnlo_aem2_exact(gamma_ns, ah, a_steps[step], aem_list[step], nf) + return res raise NotImplementedError("Selected order is not implemented") From 1f0de41277002ed802db5c0afdd7c9b6315448ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 5 Oct 2022 14:37:03 +0200 Subject: [PATCH 188/312] Modify test_compute_aem_as --- src/eko/beta.py | 4 +- src/eko/couplings.py | 1 - tests/eko/test_couplings.py | 85 +++++++++++++++++++++++++------------ 3 files changed, 61 insertions(+), 29 deletions(-) diff --git a/src/eko/beta.py b/src/eko/beta.py index 6326aa6a0..8de604a37 100644 --- a/src/eko/beta.py +++ b/src/eko/beta.py @@ -51,7 +51,7 @@ def beta_qed_aem2(nf): """ nu = constants.uplike_flavors(nf) nd = nf - nu - nl = 3 # TODO : pass nf as an argument?? + nl = 3 # TODO : pass nl as an argument?? beta_qed_aem2 = ( -4.0 / 3 * constants.NC * (nl + nu * constants.eu2 + nd * constants.ed2) ) @@ -102,7 +102,7 @@ def beta_qed_aem3(nf): """ nu = constants.uplike_flavors(nf) nd = nf - nu - nl = 3 # TODO : pass nf as an argument?? + nl = 3 # TODO : pass nl as an argument?? beta_qed_aem3 = ( -4.0 * constants.NC * (nl + nu * constants.eu2**2 + nd * constants.ed2**2) ) diff --git a/src/eko/couplings.py b/src/eko/couplings.py index 27eb92c06..d9139bbd7 100644 --- a/src/eko/couplings.py +++ b/src/eko/couplings.py @@ -681,7 +681,6 @@ def compute_exact_fixed_alphaem(self, a_ref, nf, scale_from, scale_to): # NLO if self.order[0] >= 2: beta_vec.append(beta_qcd((3, 0), nf)) - # beta_qed_mix = beta_qed((1, 2), nf) # NNLO if self.order[0] >= 3: beta_vec.append(beta_qcd((4, 0), nf)) diff --git a/tests/eko/test_couplings.py b/tests/eko/test_couplings.py index 9939816a0..61af7ed88 100644 --- a/tests/eko/test_couplings.py +++ b/tests/eko/test_couplings.py @@ -591,30 +591,63 @@ def test_compute_aem_as(self): alphas_ref = 0.118 alphaem_ref = 0.00781 scale_ref = 91.2**2 - thresh_setups = [ - # (np.inf, np.inf, np.inf), - # (0, np.inf, np.inf), - (2, 4, 175), - ] - scale_target = [1, 5, 10, 50, 100, 500] + thresh_setup = (2, 4, 175) + scale_target = [10, 50, 100] # nf = 5 + for running_alphaem in [False]: + for qcd in range(1, 4 + 1): + for qed in range(0, 2 + 1): + couplings = Couplings( + np.array([alphas_ref, alphaem_ref]), + scale_ref, + thresh_setup, + (1.0, 1.0, 1.0), + (qcd, qed), + "exact", + alphaem_running=running_alphaem, + ) + for Qf in scale_target: + a_values = [] + a_values.append(couplings.a(Qf**2)) + for a in a_values: + aem = couplings.compute_aem_as(a[0], nf=5) + np.testing.assert_allclose( + aem, a[1], atol=1e-10, rtol=1e-10 + ) for running_alphaem in [True, False]: - for thresh_setup in thresh_setups: - for qcd in range(1, 4 + 1): - for qed in range(0, 2 + 1): - couplings = Couplings( - np.array([alphas_ref, alphaem_ref]), - scale_ref, - thresh_setup, - (1.0, 1.0, 1.0), - (qcd, qed), - "exact", - alphaem_running=running_alphaem, - ) - for Qf in scale_target: - a_values = [] - a_values.append(couplings.a(Qf**2)) - for a in a_values: - aem = couplings.compute_aem_as(a[0], nf=5) - np.testing.assert_allclose( - aem, a[1], atol=1e-6, rtol=1e-2 - ) + for qcd in range(1, 4 + 1): + for qed in range(0, 0 + 1): + couplings = Couplings( + np.array([alphas_ref, alphaem_ref]), + scale_ref, + thresh_setup, + (1.0, 1.0, 1.0), + (qcd, qed), + "exact", + alphaem_running=running_alphaem, + ) + for Qf in scale_target: + a_values = [] + a_values.append(couplings.a(Qf**2)) + for a in a_values: + aem = couplings.compute_aem_as(a[0], nf=5) + np.testing.assert_allclose( + aem, a[1], atol=1e-10, rtol=1e-10 + ) + for running_alphaem in [True]: + for qcd in range(1, 4 + 1): + for qed in range(1, 2 + 1): + couplings = Couplings( + np.array([alphas_ref, alphaem_ref]), + scale_ref, + thresh_setup, + (1.0, 1.0, 1.0), + (qcd, qed), + "exact", + alphaem_running=running_alphaem, + ) + for Qf in scale_target: + a_values = [] + a_values.append(couplings.a(Qf**2)) + for a in a_values: + aem = couplings.compute_aem_as(a[0], nf=5) + np.testing.assert_allclose(aem, a[1], atol=1e-6, rtol=1e-2) From dceed98f16034fe553c7898dcbd60d673902b909 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 5 Oct 2022 16:50:27 +0200 Subject: [PATCH 189/312] Add nf_ref to test_compute_aem_as --- tests/eko/test_couplings.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/eko/test_couplings.py b/tests/eko/test_couplings.py index 61af7ed88..30d24d619 100644 --- a/tests/eko/test_couplings.py +++ b/tests/eko/test_couplings.py @@ -603,6 +603,7 @@ def test_compute_aem_as(self): (1.0, 1.0, 1.0), (qcd, qed), "exact", + nf_ref=5, alphaem_running=running_alphaem, ) for Qf in scale_target: @@ -623,6 +624,7 @@ def test_compute_aem_as(self): (1.0, 1.0, 1.0), (qcd, qed), "exact", + nf_ref=5, alphaem_running=running_alphaem, ) for Qf in scale_target: @@ -643,6 +645,7 @@ def test_compute_aem_as(self): (1.0, 1.0, 1.0), (qcd, qed), "exact", + nf_ref=5, alphaem_running=running_alphaem, ) for Qf in scale_target: From 7557a367c88c3c7568fe2aa925a7b901157a7637 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 6 Oct 2022 16:22:29 +0200 Subject: [PATCH 190/312] Modify test_couplings --- tests/eko/test_couplings.py | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/tests/eko/test_couplings.py b/tests/eko/test_couplings.py index 30d24d619..d3944c927 100644 --- a/tests/eko/test_couplings.py +++ b/tests/eko/test_couplings.py @@ -592,7 +592,7 @@ def test_compute_aem_as(self): alphaem_ref = 0.00781 scale_ref = 91.2**2 thresh_setup = (2, 4, 175) - scale_target = [10, 50, 100] # nf = 5 + scale_target = [5, 10, 50, 100, 150] # nf = 5 for running_alphaem in [False]: for qcd in range(1, 4 + 1): for qed in range(0, 2 + 1): @@ -606,14 +606,12 @@ def test_compute_aem_as(self): nf_ref=5, alphaem_running=running_alphaem, ) + a_values = [] for Qf in scale_target: - a_values = [] a_values.append(couplings.a(Qf**2)) - for a in a_values: - aem = couplings.compute_aem_as(a[0], nf=5) - np.testing.assert_allclose( - aem, a[1], atol=1e-10, rtol=1e-10 - ) + for a in a_values: + aem = couplings.compute_aem_as(a[0], nf=5) + np.testing.assert_allclose(aem, a[1], atol=1e-10, rtol=1e-10) for running_alphaem in [True, False]: for qcd in range(1, 4 + 1): for qed in range(0, 0 + 1): @@ -627,14 +625,12 @@ def test_compute_aem_as(self): nf_ref=5, alphaem_running=running_alphaem, ) + a_values = [] for Qf in scale_target: - a_values = [] a_values.append(couplings.a(Qf**2)) - for a in a_values: - aem = couplings.compute_aem_as(a[0], nf=5) - np.testing.assert_allclose( - aem, a[1], atol=1e-10, rtol=1e-10 - ) + for a in a_values: + aem = couplings.compute_aem_as(a[0], nf=5) + np.testing.assert_allclose(aem, a[1], atol=1e-10, rtol=1e-10) for running_alphaem in [True]: for qcd in range(1, 4 + 1): for qed in range(1, 2 + 1): @@ -648,9 +644,9 @@ def test_compute_aem_as(self): nf_ref=5, alphaem_running=running_alphaem, ) + a_values = [] for Qf in scale_target: - a_values = [] a_values.append(couplings.a(Qf**2)) - for a in a_values: - aem = couplings.compute_aem_as(a[0], nf=5) - np.testing.assert_allclose(aem, a[1], atol=1e-6, rtol=1e-2) + for a in a_values: + aem = couplings.compute_aem_as(a[0], nf=5) + np.testing.assert_allclose(aem, a[1], atol=1e-6, rtol=1e-2) From 996330342528ac29a04f43215c6b0b082fe72cb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 6 Oct 2022 22:54:16 +0200 Subject: [PATCH 191/312] Fix compute_aem_as --- src/eko/couplings.py | 6 +++--- src/eko/evolution_operator/__init__.py | 27 +++++++++++++++++++++----- tests/eko/test_couplings.py | 12 +++++++++--- tests/eko/test_ev_operator.py | 7 ++++++- 4 files changed, 40 insertions(+), 12 deletions(-) diff --git a/src/eko/couplings.py b/src/eko/couplings.py index d9139bbd7..cb789c7bf 100644 --- a/src/eko/couplings.py +++ b/src/eko/couplings.py @@ -700,7 +700,7 @@ def compute_exact_fixed_alphaem(self, a_ref, nf, scale_from, scale_to): ) return np.array([rge_qcd, a_ref[1]]) - def compute_aem_as(self, as_to, nf): + def compute_aem_as(self, aem_ref, as_from, as_to, nf): """Compute :math:`a_{em}` as a function of :math:`a_s`. Parameters @@ -751,8 +751,8 @@ def rge(_as, a_em, beta_qcd_vec, beta_qcd_mix, beta_qed_vec, beta_qed_mix): res = scipy.integrate.solve_ivp( rge, - (self.a_ref[0], as_to), - (self.a_ref[1],), + (as_from, as_to), + (aem_ref,), args=[beta_qcd_vec, beta_qcd_mix, beta_qed_vec, beta_qed_mix], method="Radau", rtol=1e-6, diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index a6ecaf07b..4e104543f 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -465,7 +465,7 @@ def a_s(self): ) a1 = sc.a_s(self.mur2_shift(self.q2_to), fact_scale=self.q2_to, nf_to=self.nf) return (a0, a1) - + @property def a_em(self): """Return the computed values for :math:`a_{em}`.""" @@ -481,16 +481,33 @@ def aem_list_as(self): """Return the list of the couplings for the different values of :math:`a_s`.""" if self.order[1] == 0: return np.array([]) - a0 = self.a_s[0] - a1 = self.a_s[1] + as0 = self.a_s[0] + as1 = self.a_s[1] + aem0 = self.a_em[0] + aem1 = self.a_em[1] + q2ref = self.managers["couplings"].q2_ref + delta_from = abs(self.q2_from - q2ref) + delta_to = abs(self.q2_to - q2ref) + # I compute the values in aem_list_as starting from the as + # that is closer to as_ref. + if delta_from > delta_to: + as_start = as1 + aem_start = aem1 + else: + as_start = as0 + aem_start = aem0 sc = self.managers["couplings"] ev_op_iterations = self.config["ev_op_iterations"] - as_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) + as_steps = utils.geomspace(as0, as1, 1 + ev_op_iterations) as_l = as_steps[0] aem_list = [] for as_h in as_steps[1:]: as_half = (as_h + as_l) / 2.0 - aem_list.append(sc.compute_aem_as(as_to=as_half, nf=self.nf)) + aem_list.append( + sc.compute_aem_as( + aem_ref=aem_start, as_from=as_start, as_to=as_half, nf=self.nf + ) + ) as_l = as_h return np.array(aem_list) diff --git a/tests/eko/test_couplings.py b/tests/eko/test_couplings.py index d3944c927..0f395d2be 100644 --- a/tests/eko/test_couplings.py +++ b/tests/eko/test_couplings.py @@ -610,7 +610,9 @@ def test_compute_aem_as(self): for Qf in scale_target: a_values.append(couplings.a(Qf**2)) for a in a_values: - aem = couplings.compute_aem_as(a[0], nf=5) + aem = couplings.compute_aem_as( + alphaem_ref / 4 / np.pi, alphas_ref / 4 / np.pi, a[0], nf=5 + ) np.testing.assert_allclose(aem, a[1], atol=1e-10, rtol=1e-10) for running_alphaem in [True, False]: for qcd in range(1, 4 + 1): @@ -629,7 +631,9 @@ def test_compute_aem_as(self): for Qf in scale_target: a_values.append(couplings.a(Qf**2)) for a in a_values: - aem = couplings.compute_aem_as(a[0], nf=5) + aem = couplings.compute_aem_as( + alphaem_ref / 4 / np.pi, alphas_ref / 4 / np.pi, a[0], nf=5 + ) np.testing.assert_allclose(aem, a[1], atol=1e-10, rtol=1e-10) for running_alphaem in [True]: for qcd in range(1, 4 + 1): @@ -648,5 +652,7 @@ def test_compute_aem_as(self): for Qf in scale_target: a_values.append(couplings.a(Qf**2)) for a in a_values: - aem = couplings.compute_aem_as(a[0], nf=5) + aem = couplings.compute_aem_as( + alphaem_ref / 4 / np.pi, alphas_ref / 4 / np.pi, a[0], nf=5 + ) np.testing.assert_allclose(aem, a[1], atol=1e-6, rtol=1e-2) diff --git a/tests/eko/test_ev_operator.py b/tests/eko/test_ev_operator.py index da338c246..6ca1c4809 100644 --- a/tests/eko/test_ev_operator.py +++ b/tests/eko/test_ev_operator.py @@ -448,7 +448,12 @@ def test_aem_list(self): as_l = as_steps[0] for step, as_h in enumerate(as_steps[1:]): as_half = (as_h + as_l) / 2.0 - aem = couplings.compute_aem_as(as_half, 3) + aem = couplings.compute_aem_as( + tcard["alphaem"] / 4 / np.pi, + tcard["alphas"] / 4 / np.pi, + as_half, + 3, + ) np.testing.assert_allclose(aem, aem_list[step]) as_l = as_h From 14d39c953d90229f38b692d12cca44a8d5ed78db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 7 Oct 2022 11:35:48 +0200 Subject: [PATCH 192/312] Add alphaem_running flag in logger --- src/eko/evolution_operator/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 4e104543f..8ea12269d 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -705,7 +705,11 @@ def compute(self): "%s: a_s distance: %e -> %e", self.log_label, self.a_s[0], self.a_s[1] ) logger.info( - "%s: a_em distance: %e -> %e", self.log_label, self.a_em[0], self.a_em[1] + "%s: a_em distance: %e -> %e, running alphaem: %r", + self.log_label, + self.a_em[0], + self.a_em[1], + self.managers["couplings"].alphaem_running, ) logger.info( "%s: order: (%d, %d), solution strategy: %s", From 3e10c369c94b7e1ffac83774b4c44621dfc0fb07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Sun, 9 Oct 2022 12:27:52 +0200 Subject: [PATCH 193/312] Add function a(self) in evolution_operator --- src/eko/evolution_operator/__init__.py | 39 ++++++++++++++------------ tests/eko/test_couplings.py | 21 ++++++++++++++ 2 files changed, 42 insertions(+), 18 deletions(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 8ea12269d..acf6a3a5f 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -457,24 +457,26 @@ def mur2_shift(self, q2): return q2 @property - def a_s(self): - """Return the computed values for :math:`a_s`.""" - sc = self.managers["couplings"] - a0 = sc.a_s( + def a(self): + """Return the computed values for :math:`a_s` and :math:`a_{em}`.""" + coupling = self.managers["couplings"] + a0 = coupling.a( self.mur2_shift(self.q2_from), fact_scale=self.q2_from, nf_to=self.nf ) - a1 = sc.a_s(self.mur2_shift(self.q2_to), fact_scale=self.q2_to, nf_to=self.nf) + a1 = coupling.a( + self.mur2_shift(self.q2_to), fact_scale=self.q2_to, nf_to=self.nf + ) return (a0, a1) + @property + def a_s(self): + """Return the computed values for :math:`a_s`.""" + return (self.a[0][0], self.a[1][0]) + @property def a_em(self): """Return the computed values for :math:`a_{em}`.""" - emc = self.managers["couplings"] - a0 = emc.a_em( - self.mur2_shift(self.q2_from), fact_scale=self.q2_from, nf_to=self.nf - ) - a1 = emc.a_em(self.mur2_shift(self.q2_to), fact_scale=self.q2_to, nf_to=self.nf) - return (a0, a1) + return (self.a[0][1], self.a[1][1]) @property def aem_list_as(self): @@ -704,13 +706,14 @@ def compute(self): logger.info( "%s: a_s distance: %e -> %e", self.log_label, self.a_s[0], self.a_s[1] ) - logger.info( - "%s: a_em distance: %e -> %e, running alphaem: %r", - self.log_label, - self.a_em[0], - self.a_em[1], - self.managers["couplings"].alphaem_running, - ) + if self.order[1] > 0: + logger.info( + "%s: a_em distance: %e -> %e, running alphaem: %r", + self.log_label, + self.a_em[0], + self.a_em[1], + self.managers["couplings"].alphaem_running, + ) logger.info( "%s: order: (%d, %d), solution strategy: %s", self.log_label, diff --git a/tests/eko/test_couplings.py b/tests/eko/test_couplings.py index 0f395d2be..cda332065 100644 --- a/tests/eko/test_couplings.py +++ b/tests/eko/test_couplings.py @@ -656,3 +656,24 @@ def test_compute_aem_as(self): alphaem_ref / 4 / np.pi, alphas_ref / 4 / np.pi, a[0], nf=5 ) np.testing.assert_allclose(aem, a[1], atol=1e-6, rtol=1e-2) + scale_target = [2.1, 2.5, 3.0, 3.5] # nf = 4 + for running_alphaem in [True]: + for qcd in range(1, 4 + 1): + for qed in range(1, 2 + 1): + couplings = Couplings( + np.array([alphas_ref, alphaem_ref]), + scale_ref, + thresh_setup, + (1.0, 1.0, 1.0), + (qcd, qed), + "exact", + nf_ref=5, + alphaem_running=running_alphaem, + ) + a_ref = couplings.a(4.0**2, nf_to=4) + a_values = [] + for Qf in scale_target: + a_values.append(couplings.a(Qf**2)) + for a in a_values: + aem = couplings.compute_aem_as(a_ref[1], a_ref[0], a[0], nf=4) + np.testing.assert_allclose(aem, a[1], atol=1e-6, rtol=1e-4) From b89329b876a691ba0f757490aa81f376392946db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 11 Oct 2022 11:59:02 +0200 Subject: [PATCH 194/312] Test all code --- tests/eko/test_couplings.py | 33 ++++++++++++++++++ tests/eko/test_ev_operator.py | 63 ++++++++++++++++++++--------------- 2 files changed, 69 insertions(+), 27 deletions(-) diff --git a/tests/eko/test_couplings.py b/tests/eko/test_couplings.py index cda332065..154fe5f8e 100644 --- a/tests/eko/test_couplings.py +++ b/tests/eko/test_couplings.py @@ -677,3 +677,36 @@ def test_compute_aem_as(self): for a in a_values: aem = couplings.compute_aem_as(a_ref[1], a_ref[0], a[0], nf=4) np.testing.assert_allclose(aem, a[1], atol=1e-6, rtol=1e-4) + + def test_as_aem(self): + # prepare + thresh_setups = [ + (np.inf, np.inf, np.inf), + (0, np.inf, np.inf), + (2, 4, 175), + ] + alphas_ref = 0.118 + alphaem_ref = 0.00781 + scale_ref = 91.0**2 + for thresh_setup in thresh_setups: + for qcd in range(1, 4 + 1): + for qed in range(2+1): + for mode_ev in ["expanded", "exact"]: + for q2 in [1.**2, 3.**2, 4.**2, 10.**2, 50**2, 100**2, 150.**2, 180.**2]: + for fact_scale_ratio in [0.5**2, 1.**2, 2.**2]: + for alphaem_running in [True, False]: + coupling = Couplings( + np.array([alphas_ref, alphaem_ref]), + scale_ref, + thresh_setup, + (1.0, 1.0, 1.0), + (qcd, qed), + mode_ev, + alphaem_running=alphaem_running, + ) + np.testing.assert_allclose( + coupling.a(q2, fact_scale=q2*fact_scale_ratio)[0], coupling.a_s(q2,fact_scale=q2*fact_scale_ratio), rtol=1e-10 + ) + np.testing.assert_allclose( + coupling.a(q2,fact_scale=q2*fact_scale_ratio)[1], coupling.a_em(q2,fact_scale=q2*fact_scale_ratio), rtol=1e-10 + ) \ No newline at end of file diff --git a/tests/eko/test_ev_operator.py b/tests/eko/test_ev_operator.py index 6ca1c4809..5b52bdbfb 100644 --- a/tests/eko/test_ev_operator.py +++ b/tests/eko/test_ev_operator.py @@ -429,33 +429,36 @@ def test_aem_list(self): ocard["ev_op_iterations"] = 10 for qcd in range(1, 3 + 1): for qed in range(1, 2 + 1): - for aem_running in [True, False]: - tcard["order"] = (qcd, qed) - tcard["alphaem_running"] = aem_running - g = OperatorGrid.from_dict( - tcard, - ocard, - ThresholdsAtlas.from_dict(tcard), - Couplings.from_dict(tcard), - InterpolatorDispatcher.from_dict(ocard), - ) - o = Operator(g.config, g.managers, 3, 2.0, 2.0) - couplings = Couplings.from_dict(tcard) - aem_list = o.aem_list_as - (a0, a1) = o.a_s - ev_op_iterations = ocard["ev_op_iterations"] - as_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) - as_l = as_steps[0] - for step, as_h in enumerate(as_steps[1:]): - as_half = (as_h + as_l) / 2.0 - aem = couplings.compute_aem_as( - tcard["alphaem"] / 4 / np.pi, - tcard["alphas"] / 4 / np.pi, - as_half, - 3, - ) - np.testing.assert_allclose(aem, aem_list[step]) - as_l = as_h + for q0 in [np.sqrt(2.), 2., 4.5]: + for q2to in ocard["Q2grid"]: + for aem_running in [True, False]: + tcard["order"] = (qcd, qed) + tcard["alphaem_running"] = aem_running + tcard["Q0"] = q0 + g = OperatorGrid.from_dict( + tcard, + ocard, + ThresholdsAtlas.from_dict(tcard), + Couplings.from_dict(tcard), + InterpolatorDispatcher.from_dict(ocard), + ) + o = Operator(g.config, g.managers, 3, q0**2, q2to) + couplings = Couplings.from_dict(tcard) + aem_list = o.aem_list_as + (a0, a1) = o.a_s + ev_op_iterations = ocard["ev_op_iterations"] + as_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) + as_l = as_steps[0] + for step, as_h in enumerate(as_steps[1:]): + as_half = (as_h + as_l) / 2.0 + aem = couplings.compute_aem_as( + tcard["alphaem"] / 4 / np.pi, + tcard["alphas"] / 4 / np.pi, + as_half, + 3, + ) + np.testing.assert_allclose(aem, aem_list[step], rtol=3e-4) + as_l = as_h def check_lo(self, o): assert (br.non_singlet_pids_map["ns-"], 0) in o.op_members @@ -525,6 +528,12 @@ def test_compute(self, monkeypatch): o.op_members[(br.non_singlet_pids_map["nsV"], 0)].value, o.op_members[(br.non_singlet_pids_map["ns-"], 0)].value, ) + o.order = (3, 1) + o.compute() + assert not np.allclose( + o.op_members[(br.non_singlet_pids_map["ns+u"], 0)].value, + o.op_members[(br.non_singlet_pids_map["ns-u"], 0)].value, + ) # unity operators for n in range(1, 3 + 1): From 6c867870603149bef571fa9824deb2bb86e69e4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 13 Oct 2022 12:27:15 +0200 Subject: [PATCH 195/312] Add exact LO solution for unidimensiona_exact --- src/eko/anomalous_dimensions/__init__.py | 5 ++--- src/eko/couplings.py | 4 +++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index 5df2fb3d6..f5e00104a 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -447,7 +447,7 @@ def gamma_singlet_qed(order, n, nf): gamma_s[2, 0] = as2.gamma_QEDsinglet(n, nf, sx) if order[1] >= 2: gamma_s[0, 2] = aem2.gamma_singlet(n, nf, sx) - if order[0] == 3: + if order[0] >= 3: gamma_s[3, 0] = as3.gamma_QEDsinglet(n, nf, sx) return gamma_s @@ -499,7 +499,6 @@ def gamma_valence_qed(order, n, nf): gamma_v[2, 0] = as2.gamma_QEDvalence(n, nf, sx) if order[1] >= 2: gamma_v[0, 2] = aem2.gamma_valence(n, nf, sx) - if order[0] == 3: - sx = np.append(sx, harmonics.S4(n)) + if order[0] >= 3: gamma_v[3, 0] = as3.gamma_QEDvalence(n, nf, sx) return gamma_v diff --git a/src/eko/couplings.py b/src/eko/couplings.py index cb789c7bf..72113b378 100644 --- a/src/eko/couplings.py +++ b/src/eko/couplings.py @@ -552,7 +552,9 @@ def unidimensional_exact(self, beta0, b_vec, u, a_ref, method, rtol): float coupling at target scale :math:`a(Q^2)` """ - + if len(b_vec) == 1 : + return exact_lo(a_ref, beta0, u) + def rge(_t, a, b_vec): rge = -(a**2) * (np.sum([a**k * b for k, b in enumerate(b_vec)])) return rge From b35206250683fb7a0c325ed30c06bc8421252b7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 14 Oct 2022 14:51:24 +0200 Subject: [PATCH 196/312] Fix bug in beta_aem2 and beta_aem3 --- src/eko/beta.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/eko/beta.py b/src/eko/beta.py index 8de604a37..219fbc3dc 100644 --- a/src/eko/beta.py +++ b/src/eko/beta.py @@ -53,7 +53,7 @@ def beta_qed_aem2(nf): nd = nf - nu nl = 3 # TODO : pass nl as an argument?? beta_qed_aem2 = ( - -4.0 / 3 * constants.NC * (nl + nu * constants.eu2 + nd * constants.ed2) + -4.0 / 3 * (nl + constants.NC * (nu * constants.eu2 + nd * constants.ed2) ) ) return beta_qed_aem2 @@ -104,7 +104,7 @@ def beta_qed_aem3(nf): nd = nf - nu nl = 3 # TODO : pass nl as an argument?? beta_qed_aem3 = ( - -4.0 * constants.NC * (nl + nu * constants.eu2**2 + nd * constants.ed2**2) + -4.0 * (nl + constants.NC * ( nu * constants.eu2**2 + nd * constants.ed2**2) ) ) return beta_qed_aem3 From 31b409b431c51531773ba9729cb055bcd4aaff74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 14 Oct 2022 14:57:33 +0200 Subject: [PATCH 197/312] Fix test_beta --- tests/eko/test_beta.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/eko/test_beta.py b/tests/eko/test_beta.py index 95f804d06..33533e2cc 100644 --- a/tests/eko/test_beta.py +++ b/tests/eko/test_beta.py @@ -29,7 +29,7 @@ def test_beta_aem2(): """Test first beta function coefficient""" # from hep-ph/9803211 np.testing.assert_approx_equal( - beta.beta_qed_aem2(5), -4.0 / 3 * 3 * (3 + 2 * 4 / 9 + 3 * 1 / 9) + beta.beta_qed_aem2(5), -4.0 / 3 * (3 + 3*(2 * 4 / 9 + 3 * 1 / 9)) ) @@ -44,7 +44,7 @@ def test_beta_aem3(): """Test second beta function coefficient""" # from hep-ph/9803211 np.testing.assert_approx_equal( - beta.beta_qed_aem3(5), -4.0 * 3 * (3 + 2 * 16 / 81 + 3 * 1 / 81) + beta.beta_qed_aem3(5), -4.0 * (3 + 3*(2 * 16 / 81 + 3 * 1 / 81)) ) From 96cfeb7cc22c8cf480fa27673c59ed423b3cad8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 17 Oct 2022 16:03:24 +0200 Subject: [PATCH 198/312] Add factor CA to gamma_{qg,phg,gg} at O(as1aem1) --- src/eko/anomalous_dimensions/as1aem1.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/eko/anomalous_dimensions/as1aem1.py b/src/eko/anomalous_dimensions/as1aem1.py index 10d6f976c..a070264af 100644 --- a/src/eko/anomalous_dimensions/as1aem1.py +++ b/src/eko/anomalous_dimensions/as1aem1.py @@ -149,7 +149,7 @@ def gamma_phg(N): O(as1aem1) photon-gluon anomalous dimension :math:`\\gamma_{\\gamma g}^{(1,1)}(N)` """ - return constants.TR / constants.CF / constants.CA * gamma_gph(N) + return constants.TR / constants.CF * gamma_gph(N) @nb.njit(cache=True) @@ -174,7 +174,7 @@ def gamma_qg(N, nf, sx): :math:`\\gamma_{qg}^{(1,1)}(N)` """ - return constants.TR / constants.CF / constants.CA * gamma_qph(N, nf, sx) + return constants.TR / constants.CF * gamma_qph(N, nf, sx) @nb.njit(cache=True) @@ -236,7 +236,7 @@ def gamma_gg(): :math:`\\gamma_{gg}^{(1,1)}(N)` """ - return 4.0 * constants.TR + return 4.0 * constants.TR * constants.CA @nb.njit(cache=True) From 32e29bb90acd6f0aa3e19876dcaa95990b292c91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 17 Oct 2022 16:50:09 +0200 Subject: [PATCH 199/312] Put nl to 0 --- src/eko/beta.py | 4 ++-- tests/eko/test_beta.py | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/eko/beta.py b/src/eko/beta.py index 219fbc3dc..be4e424ec 100644 --- a/src/eko/beta.py +++ b/src/eko/beta.py @@ -51,7 +51,7 @@ def beta_qed_aem2(nf): """ nu = constants.uplike_flavors(nf) nd = nf - nu - nl = 3 # TODO : pass nl as an argument?? + nl = 0. # TODO : pass nl as an argument?? beta_qed_aem2 = ( -4.0 / 3 * (nl + constants.NC * (nu * constants.eu2 + nd * constants.ed2) ) ) @@ -102,7 +102,7 @@ def beta_qed_aem3(nf): """ nu = constants.uplike_flavors(nf) nd = nf - nu - nl = 3 # TODO : pass nl as an argument?? + nl = 0. # TODO : pass nl as an argument?? beta_qed_aem3 = ( -4.0 * (nl + constants.NC * ( nu * constants.eu2**2 + nd * constants.ed2**2) ) ) diff --git a/tests/eko/test_beta.py b/tests/eko/test_beta.py index 33533e2cc..a2af38d6e 100644 --- a/tests/eko/test_beta.py +++ b/tests/eko/test_beta.py @@ -9,6 +9,7 @@ from eko import beta from eko.harmonics.constants import zeta3 +nl = 0 def _flav_test(function): """Check that the given beta function `function` is valid @@ -29,7 +30,7 @@ def test_beta_aem2(): """Test first beta function coefficient""" # from hep-ph/9803211 np.testing.assert_approx_equal( - beta.beta_qed_aem2(5), -4.0 / 3 * (3 + 3*(2 * 4 / 9 + 3 * 1 / 9)) + beta.beta_qed_aem2(5), -4.0 / 3 * (nl + 3*(2 * 4 / 9 + 3 * 1 / 9)) ) @@ -44,7 +45,7 @@ def test_beta_aem3(): """Test second beta function coefficient""" # from hep-ph/9803211 np.testing.assert_approx_equal( - beta.beta_qed_aem3(5), -4.0 * (3 + 3*(2 * 16 / 81 + 3 * 1 / 81)) + beta.beta_qed_aem3(5), -4.0 * (nl + 3*(2 * 16 / 81 + 3 * 1 / 81)) ) From 6c7fb8b890501b8fce839a808088eb90b2b2f89d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 18 Oct 2022 15:29:28 +0200 Subject: [PATCH 200/312] Revert "Put nl to 0" This reverts commit 32e29bb90acd6f0aa3e19876dcaa95990b292c91. --- src/eko/beta.py | 4 ++-- tests/eko/test_beta.py | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/eko/beta.py b/src/eko/beta.py index be4e424ec..219fbc3dc 100644 --- a/src/eko/beta.py +++ b/src/eko/beta.py @@ -51,7 +51,7 @@ def beta_qed_aem2(nf): """ nu = constants.uplike_flavors(nf) nd = nf - nu - nl = 0. # TODO : pass nl as an argument?? + nl = 3 # TODO : pass nl as an argument?? beta_qed_aem2 = ( -4.0 / 3 * (nl + constants.NC * (nu * constants.eu2 + nd * constants.ed2) ) ) @@ -102,7 +102,7 @@ def beta_qed_aem3(nf): """ nu = constants.uplike_flavors(nf) nd = nf - nu - nl = 0. # TODO : pass nl as an argument?? + nl = 3 # TODO : pass nl as an argument?? beta_qed_aem3 = ( -4.0 * (nl + constants.NC * ( nu * constants.eu2**2 + nd * constants.ed2**2) ) ) diff --git a/tests/eko/test_beta.py b/tests/eko/test_beta.py index a2af38d6e..33533e2cc 100644 --- a/tests/eko/test_beta.py +++ b/tests/eko/test_beta.py @@ -9,7 +9,6 @@ from eko import beta from eko.harmonics.constants import zeta3 -nl = 0 def _flav_test(function): """Check that the given beta function `function` is valid @@ -30,7 +29,7 @@ def test_beta_aem2(): """Test first beta function coefficient""" # from hep-ph/9803211 np.testing.assert_approx_equal( - beta.beta_qed_aem2(5), -4.0 / 3 * (nl + 3*(2 * 4 / 9 + 3 * 1 / 9)) + beta.beta_qed_aem2(5), -4.0 / 3 * (3 + 3*(2 * 4 / 9 + 3 * 1 / 9)) ) @@ -45,7 +44,7 @@ def test_beta_aem3(): """Test second beta function coefficient""" # from hep-ph/9803211 np.testing.assert_approx_equal( - beta.beta_qed_aem3(5), -4.0 * (nl + 3*(2 * 16 / 81 + 3 * 1 / 81)) + beta.beta_qed_aem3(5), -4.0 * (3 + 3*(2 * 16 / 81 + 3 * 1 / 81)) ) From 530ad27330bab8653f97240627b27e3272e808e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 18 Oct 2022 15:32:42 +0200 Subject: [PATCH 201/312] Modify CA -> NC in gamma_{qg,phg,gg} at O(as1aem1) --- src/eko/anomalous_dimensions/as1aem1.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/eko/anomalous_dimensions/as1aem1.py b/src/eko/anomalous_dimensions/as1aem1.py index a070264af..53fa74be3 100644 --- a/src/eko/anomalous_dimensions/as1aem1.py +++ b/src/eko/anomalous_dimensions/as1aem1.py @@ -149,7 +149,7 @@ def gamma_phg(N): O(as1aem1) photon-gluon anomalous dimension :math:`\\gamma_{\\gamma g}^{(1,1)}(N)` """ - return constants.TR / constants.CF * gamma_gph(N) + return constants.TR / constants.CF / constants.CA * constants.NC * gamma_gph(N) @nb.njit(cache=True) @@ -174,7 +174,7 @@ def gamma_qg(N, nf, sx): :math:`\\gamma_{qg}^{(1,1)}(N)` """ - return constants.TR / constants.CF * gamma_qph(N, nf, sx) + return constants.TR / constants.CF / constants.CA * constants.NC * gamma_qph(N, nf, sx) @nb.njit(cache=True) @@ -236,7 +236,7 @@ def gamma_gg(): :math:`\\gamma_{gg}^{(1,1)}(N)` """ - return 4.0 * constants.TR * constants.CA + return 4.0 * constants.TR * constants.NC @nb.njit(cache=True) From 9133790de9de9c51e0a72264bb6f2f291e6752a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 19 Oct 2022 13:44:38 +0200 Subject: [PATCH 202/312] Init decoupled couplings evolution --- src/eko/couplings.py | 124 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/src/eko/couplings.py b/src/eko/couplings.py index e25893551..8f53ce989 100644 --- a/src/eko/couplings.py +++ b/src/eko/couplings.py @@ -701,6 +701,77 @@ def compute_exact_fixed_alphaem(self, a_ref, nf, scale_from, scale_to): rtol=1e-6, ) return np.array([rge_qcd, a_ref[1]]) + + def compute_exact_decoupled_running(self, a_ref, nf, scale_qcd_from, scale_qed_from, scale_to): + """Compute couplings via |RGE| with running alphaem without the mixed terms. + + Parameters + ---------- + as_ref : numpy.ndarray + reference alpha_s and alpha + nf : int + value of nf for computing alpha_i + scale_from : float + reference scale + scale_to : float + target scale + + Returns + ------- + numpy.ndarray + couplings at target scale :math:`a(Q^2)` + """ + # when we discard the mixed terms the Qref doesn't have to be the same + u_qcd = np.log(scale_to / scale_qcd_from) + u_qed = np.log(scale_to / scale_qed_from) + + # in LO fallback to expanded, as this is the full solution + if self.order == (1, 0): + return couplings_expanded_fixed_alphaem( + self.order, a_ref, nf, scale_qcd_from, float(scale_to) + ) + + beta_qcd_vec = [beta_qcd((2, 0), nf)] + # NLO + if self.order[0] >= 2: + beta_qcd_vec.append(beta_qcd((3, 0), nf)) + # NNLO + if self.order[0] >= 3: + beta_qcd_vec.append(beta_qcd((4, 0), nf)) + # N3LO + if self.order[0] >= 4: + beta_qcd_vec.append(beta_qcd((5, 0), nf)) + b_qcd_vec = [ + beta_qcd_vec[i] / beta_qcd_vec[0] for i in range(self.order[0]) + ] + a_s = self.unidimensional_exact( + beta_qcd_vec[0], + b_qcd_vec, + u_qcd, + a_ref[0], + method="Radau", + rtol=1e-6, + ) + if self.order[1] == 0: + # for order = (qcd, 0) with qcd > 1 we return the exact solution for the QCD RGE + # while aem is constant + return np.array([a_s, a_ref[1]]) + if self.order[1] >= 1: + beta_qed_vec = [beta_qed((0, 2), nf)] + if self.order[1] >= 2: + beta_qed_vec.append(beta_qed((0, 3), nf)) + b_qed_vec = [ + beta_qed_vec[i] / beta_qed_vec[0] for i in range(self.order[1]) + ] + a_em = self.unidimensional_exact( + beta_qed_vec[0], + b_qed_vec, + u_qed, + a_ref[1], + method="Radau", + rtol=1e-6, + ) + return np.array([a_s, a_em]) def compute_aem_as(self, aem_ref, as_from, as_to, nf): """Compute :math:`a_{em}` as a function of :math:`a_s`. @@ -760,6 +831,59 @@ def rge(_as, a_em, beta_qcd_vec, beta_qcd_mix, beta_qed_vec, beta_qed_mix): rtol=1e-6, ) return res.y[0][-1] + + def compute_aem_as_decoupled(self, aem_ref, as_from, as_to, nf): + """Compute :math:`a_{em}` as a function of :math:`a_s` with no mixing terms. + + Parameters + ---------- + as_to : float + target a_s + nf : int + value of nf for computing alpha_i + + Returns + ------- + float + a_em at target a_s :math:`a_em(a_s)` + """ + if not self.alphaem_running: + return self.a_ref[1] + beta_qcd_vec = [beta_qcd((2, 0), nf)] + beta_qed_vec = [] + # NLO + if self.order[0] >= 2: + beta_qcd_vec.append(beta_qcd((3, 0), nf)) + # NNLO + if self.order[0] >= 3: + beta_qcd_vec.append(beta_qcd((4, 0), nf)) + # N3LO + if self.order[0] >= 4: + beta_qcd_vec.append(beta_qcd((5, 0), nf)) + if self.order[1] >= 1: + beta_qed_vec = [beta_qed((0, 2), nf)] + if self.order[1] >= 2: + beta_qed_vec.append(beta_qed((0, 3), nf)) + + def rge(_as, a_em, beta_qcd_vec, beta_qed_vec): + rge_qed = -(a_em**2) * ( + np.sum([a_em**k * b for k, b in enumerate(beta_qed_vec)]) + ) + rge_qcd = -(_as**2) * ( + np.sum([_as**k * b for k, b in enumerate(beta_qcd_vec)]) + ) + + return rge_qed / rge_qcd + + res = scipy.integrate.solve_ivp( + rge, + (as_from, as_to), + (aem_ref,), + args=[beta_qcd_vec, beta_qed_vec], + method="Radau", + rtol=1e-6, + ) + return res.y[0][-1] def compute(self, a_ref, nf, scale_from, scale_to): """Compute actual couplings. From c775395af92eb780ab1fab61e1c50c8a75d8f134 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 20 Oct 2022 10:17:27 +0200 Subject: [PATCH 203/312] Implement qed exponentiated scale variations --- src/eko/evolution_operator/__init__.py | 12 +-- src/eko/scale_variations/exponentiated.py | 94 +++++++++++++---------- 2 files changed, 58 insertions(+), 48 deletions(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 8c4840d2b..5531855eb 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -302,8 +302,8 @@ def quad_ker( if sv_mode == sv.Modes.exponentiated: # the aem terms do not get scale variations # TODO : double check it - gamma_s[1:, 0] = sv.exponentiated.gamma_variation( - gamma_s[1:, 0], order, nf, L + gamma_s = sv.exponentiated.gamma_variation_qed( + gamma_s, order, nf, L ) ker = qed_s.dispatcher( order, @@ -327,8 +327,8 @@ def quad_ker( gamma_v = ad.gamma_valence_qed(order, ker_base.n, nf) # scale var exponentiated is directly applied on gamma if sv_mode == sv.Modes.exponentiated: - gamma_v[1:, 0] = sv.exponentiated.gamma_variation( - gamma_v[1:, 0], order, nf, L + gamma_v = sv.exponentiated.gamma_variation_qed( + gamma_v, order, nf, L ) ker = qed_v.dispatcher( order, @@ -351,8 +351,8 @@ def quad_ker( gamma_ns = ad.gamma_ns_qed(order, mode0, ker_base.n, nf) # scale var exponentiated is directly applied on gamma if sv_mode == sv.Modes.exponentiated: - gamma_ns[1:, 0] = sv.exponentiated.gamma_variation( - gamma_ns[1:, 0], order, nf, L + gamma_ns = sv.exponentiated.gamma_variation_qed( + gamma_ns, order, nf, L ) ker = qed_ns.dispatcher( order, diff --git a/src/eko/scale_variations/exponentiated.py b/src/eko/scale_variations/exponentiated.py index 3d2e3cf94..9988bfb7a 100644 --- a/src/eko/scale_variations/exponentiated.py +++ b/src/eko/scale_variations/exponentiated.py @@ -46,47 +46,57 @@ def gamma_variation(gamma, order, nf, L): return gamma -# @nb.njit(cache=True) -# def gamma_variation_qed(gamma, order, nf, L): -# """ -# Adjust the anomalous dimensions with the scale variations. +@nb.njit(cache=True) +def gamma_variation_qed(gamma, order, nf, L): + """ + Adjust the anomalous dimensions with the scale variations. -# Parameters -# ---------- -# gamma : numpy.ndarray -# anomalous dimensions -# order : tuple(int,int) -# perturbation order -# nf : int -# number of active flavors -# L : float -# logarithmic ratio of factorization and renormalization scale + Parameters + ---------- + gamma : numpy.ndarray + anomalous dimensions + order : tuple(int,int) + perturbation order + nf : int + number of active flavors + L : float + logarithmic ratio of factorization and renormalization scale -# Returns -# ------- -# gamma : numpy.ndarray -# adjusted anomalous dimensions -# """ -# # since we are modifying *in-place* be carefull, that the order matters! -# # and indeed, we need to adjust the high elements first -# beta0 = beta.beta_qcd((2, 0), nf) -# beta1 = beta.beta_qcd((3, 0), nf) -# if order[0] >= 4: -# gamma[4, 0] -= ( -# 3 * beta0 * L * gamma[3, 0] -# + (2 * beta1 * L - 3 * beta0**2 * L**2) * gamma[2, 0] -# + ( -# beta.beta_qcd((4, 0), nf) * L -# - 5 / 2 * beta1 * beta0 * L**2 -# + beta0**3 * L**3 -# ) -# * gamma[1, 0] -# ) -# if order[0] >= 3: -# gamma[3, 0] -= ( -# 2 * beta0 * gamma[2, 0] * L -# + (beta1 * L - beta0**2 * L**2) * gamma[1, 0] -# ) -# if order[0] >= 2: -# gamma[2, 0] -= beta0 * gamma[1, 0] * L -# return gamma + Returns + ------- + gamma : numpy.ndarray + adjusted anomalous dimensions + """ + # TODO : Implement scale variations for fixed alpha_em + # since we are modifying *in-place* be carefull, that the order matters! + # and indeed, we need to adjust the high elements first + beta0qcd = beta.beta_qcd((2, 0), nf) + beta1qcd = beta.beta_qcd((3, 0), nf) + beta2qcd = beta.beta_qcd((4, 0), nf) + beta0qed = beta.beta_qed((0, 2), nf) + # beta1qed = beta.beta_qcd((0, 3), nf) + # beta01qcd = beta.beta_qcd((2, 1), nf) + if order[0] >= 4: + gamma[4, 0] -= ( + 3 * beta0qcd * L * gamma[3, 0] + + (2 * beta1qcd * L - 3 * beta0qcd**2 * L**2) * gamma[2, 0] + + ( + beta2qcd * L + - 5 / 2 * beta1qcd * beta0qcd * L**2 + + beta0qcd**3 * L**3 + ) + * gamma[1, 0] + ) + if order[0] >= 3: + gamma[3, 0] -= ( + 2 * beta0qcd * gamma[2, 0] * L + + (beta1qcd * L - beta0qcd**2 * L**2) * gamma[1, 0] + ) + if order[0] >= 2: + gamma[2, 0] -= beta0qcd * gamma[1, 0] * L + # we are neglecting the order (2,1) + # if order[1] >= 1: + # gamma[2, 1] -= -L * (beta01qcd * gamma[1,0] + beta0qcd * gamma[1,1]) + if order[1] >= 2 : + gamma[0, 2] -= beta0qed * gamma[0, 1] * L + return gamma From 1768f9b37559779b277d184b2874c326eef2237b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 20 Oct 2022 11:21:36 +0200 Subject: [PATCH 204/312] Implement QED scale var for fixed alphaem --- src/eko/couplings.py | 246 +++++++++++----------- src/eko/evolution_operator/__init__.py | 14 +- src/eko/kernels/QEDnon_singlet.py | 28 ++- src/eko/scale_variations/exponentiated.py | 79 ++++--- tests/eko/test_couplings.py | 16 +- tests/eko/test_ev_operator.py | 14 +- tests/eko/test_kernels_QEDns.py | 16 +- 7 files changed, 241 insertions(+), 172 deletions(-) diff --git a/src/eko/couplings.py b/src/eko/couplings.py index 8f53ce989..5a7a0003d 100644 --- a/src/eko/couplings.py +++ b/src/eko/couplings.py @@ -443,8 +443,6 @@ def __init__( raise ValueError(f"Unknown method {method}") self.method = method self.alphaem_running = alphaem_running - # alphaem_running is put for a future implementation of the DGLAP solution - # with running alpha. For the moment in must remain set to False # create new threshold object self.a_ref = couplings_ref.copy() / 4.0 / np.pi # convert to a_s and a_em @@ -702,76 +700,76 @@ def compute_exact_fixed_alphaem(self, a_ref, nf, scale_from, scale_to): ) return np.array([rge_qcd, a_ref[1]]) - def compute_exact_decoupled_running(self, a_ref, nf, scale_qcd_from, scale_qed_from, scale_to): - """Compute couplings via |RGE| with running alphaem without the mixed terms. - - Parameters - ---------- - as_ref : numpy.ndarray - reference alpha_s and alpha - nf : int - value of nf for computing alpha_i - scale_from : float - reference scale - scale_to : float - target scale - - Returns - ------- - numpy.ndarray - couplings at target scale :math:`a(Q^2)` - """ - # when we discard the mixed terms the Qref doesn't have to be the same - u_qcd = np.log(scale_to / scale_qcd_from) - u_qed = np.log(scale_to / scale_qed_from) - - # in LO fallback to expanded, as this is the full solution - if self.order == (1, 0): - return couplings_expanded_fixed_alphaem( - self.order, a_ref, nf, scale_qcd_from, float(scale_to) - ) - - beta_qcd_vec = [beta_qcd((2, 0), nf)] - # NLO - if self.order[0] >= 2: - beta_qcd_vec.append(beta_qcd((3, 0), nf)) - # NNLO - if self.order[0] >= 3: - beta_qcd_vec.append(beta_qcd((4, 0), nf)) - # N3LO - if self.order[0] >= 4: - beta_qcd_vec.append(beta_qcd((5, 0), nf)) - b_qcd_vec = [ - beta_qcd_vec[i] / beta_qcd_vec[0] for i in range(self.order[0]) - ] - a_s = self.unidimensional_exact( - beta_qcd_vec[0], - b_qcd_vec, - u_qcd, - a_ref[0], - method="Radau", - rtol=1e-6, - ) - if self.order[1] == 0: - # for order = (qcd, 0) with qcd > 1 we return the exact solution for the QCD RGE - # while aem is constant - return np.array([a_s, a_ref[1]]) - if self.order[1] >= 1: - beta_qed_vec = [beta_qed((0, 2), nf)] - if self.order[1] >= 2: - beta_qed_vec.append(beta_qed((0, 3), nf)) - b_qed_vec = [ - beta_qed_vec[i] / beta_qed_vec[0] for i in range(self.order[1]) - ] - a_em = self.unidimensional_exact( - beta_qed_vec[0], - b_qed_vec, - u_qed, - a_ref[1], - method="Radau", - rtol=1e-6, - ) - return np.array([a_s, a_em]) + # def compute_exact_decoupled_running(self, a_ref, nf, scale_qcd_from, scale_qed_from, scale_to): + # """Compute couplings via |RGE| with running alphaem without the mixed terms. + + # Parameters + # ---------- + # as_ref : numpy.ndarray + # reference alpha_s and alpha + # nf : int + # value of nf for computing alpha_i + # scale_from : float + # reference scale + # scale_to : float + # target scale + + # Returns + # ------- + # numpy.ndarray + # couplings at target scale :math:`a(Q^2)` + # """ + # # when we discard the mixed terms the Qref doesn't have to be the same + # u_qcd = np.log(scale_to / scale_qcd_from) + # u_qed = np.log(scale_to / scale_qed_from) + + # # in LO fallback to expanded, as this is the full solution + # if self.order == (1, 0): + # return couplings_expanded_fixed_alphaem( + # self.order, a_ref, nf, scale_qcd_from, float(scale_to) + # ) + + # beta_qcd_vec = [beta_qcd((2, 0), nf)] + # # NLO + # if self.order[0] >= 2: + # beta_qcd_vec.append(beta_qcd((3, 0), nf)) + # # NNLO + # if self.order[0] >= 3: + # beta_qcd_vec.append(beta_qcd((4, 0), nf)) + # # N3LO + # if self.order[0] >= 4: + # beta_qcd_vec.append(beta_qcd((5, 0), nf)) + # b_qcd_vec = [ + # beta_qcd_vec[i] / beta_qcd_vec[0] for i in range(self.order[0]) + # ] + # a_s = self.unidimensional_exact( + # beta_qcd_vec[0], + # b_qcd_vec, + # u_qcd, + # a_ref[0], + # method="Radau", + # rtol=1e-6, + # ) + # if self.order[1] == 0: + # # for order = (qcd, 0) with qcd > 1 we return the exact solution for the QCD RGE + # # while aem is constant + # return np.array([a_s, a_ref[1]]) + # if self.order[1] >= 1: + # beta_qed_vec = [beta_qed((0, 2), nf)] + # if self.order[1] >= 2: + # beta_qed_vec.append(beta_qed((0, 3), nf)) + # b_qed_vec = [ + # beta_qed_vec[i] / beta_qed_vec[0] for i in range(self.order[1]) + # ] + # a_em = self.unidimensional_exact( + # beta_qed_vec[0], + # b_qed_vec, + # u_qed, + # a_ref[1], + # method="Radau", + # rtol=1e-6, + # ) + # return np.array([a_s, a_em]) def compute_aem_as(self, aem_ref, as_from, as_to, nf): """Compute :math:`a_{em}` as a function of :math:`a_s`. @@ -832,58 +830,58 @@ def rge(_as, a_em, beta_qcd_vec, beta_qcd_mix, beta_qed_vec, beta_qed_mix): ) return res.y[0][-1] - def compute_aem_as_decoupled(self, aem_ref, as_from, as_to, nf): - """Compute :math:`a_{em}` as a function of :math:`a_s` with no mixing terms. - - Parameters - ---------- - as_to : float - target a_s - nf : int - value of nf for computing alpha_i - - Returns - ------- - float - a_em at target a_s :math:`a_em(a_s)` - """ - if not self.alphaem_running: - return self.a_ref[1] - beta_qcd_vec = [beta_qcd((2, 0), nf)] - beta_qed_vec = [] - # NLO - if self.order[0] >= 2: - beta_qcd_vec.append(beta_qcd((3, 0), nf)) - # NNLO - if self.order[0] >= 3: - beta_qcd_vec.append(beta_qcd((4, 0), nf)) - # N3LO - if self.order[0] >= 4: - beta_qcd_vec.append(beta_qcd((5, 0), nf)) - if self.order[1] >= 1: - beta_qed_vec = [beta_qed((0, 2), nf)] - if self.order[1] >= 2: - beta_qed_vec.append(beta_qed((0, 3), nf)) - - def rge(_as, a_em, beta_qcd_vec, beta_qed_vec): - rge_qed = -(a_em**2) * ( - np.sum([a_em**k * b for k, b in enumerate(beta_qed_vec)]) - ) - rge_qcd = -(_as**2) * ( - np.sum([_as**k * b for k, b in enumerate(beta_qcd_vec)]) - ) - - return rge_qed / rge_qcd - - res = scipy.integrate.solve_ivp( - rge, - (as_from, as_to), - (aem_ref,), - args=[beta_qcd_vec, beta_qed_vec], - method="Radau", - rtol=1e-6, - ) - return res.y[0][-1] + # def compute_aem_as_decoupled(self, aem_ref, as_from, as_to, nf): + # """Compute :math:`a_{em}` as a function of :math:`a_s` with no mixing terms. + + # Parameters + # ---------- + # as_to : float + # target a_s + # nf : int + # value of nf for computing alpha_i + + # Returns + # ------- + # float + # a_em at target a_s :math:`a_em(a_s)` + # """ + # if not self.alphaem_running: + # return self.a_ref[1] + # beta_qcd_vec = [beta_qcd((2, 0), nf)] + # beta_qed_vec = [] + # # NLO + # if self.order[0] >= 2: + # beta_qcd_vec.append(beta_qcd((3, 0), nf)) + # # NNLO + # if self.order[0] >= 3: + # beta_qcd_vec.append(beta_qcd((4, 0), nf)) + # # N3LO + # if self.order[0] >= 4: + # beta_qcd_vec.append(beta_qcd((5, 0), nf)) + # if self.order[1] >= 1: + # beta_qed_vec = [beta_qed((0, 2), nf)] + # if self.order[1] >= 2: + # beta_qed_vec.append(beta_qed((0, 3), nf)) + + # def rge(_as, a_em, beta_qcd_vec, beta_qed_vec): + # rge_qed = -(a_em**2) * ( + # np.sum([a_em**k * b for k, b in enumerate(beta_qed_vec)]) + # ) + # rge_qcd = -(_as**2) * ( + # np.sum([_as**k * b for k, b in enumerate(beta_qcd_vec)]) + # ) + + # return rge_qed / rge_qcd + + # res = scipy.integrate.solve_ivp( + # rge, + # (as_from, as_to), + # (aem_ref,), + # args=[beta_qcd_vec, beta_qed_vec], + # method="Radau", + # rtol=1e-6, + # ) + # return res.y[0][-1] def compute(self, a_ref, nf, scale_from, scale_to): """Compute actual couplings. diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 5531855eb..fe1ed771c 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -196,6 +196,7 @@ def quad_ker( as1, as0, aem_list, + alphaem_running, nf, L, ev_op_iterations, @@ -303,7 +304,7 @@ def quad_ker( # the aem terms do not get scale variations # TODO : double check it gamma_s = sv.exponentiated.gamma_variation_qed( - gamma_s, order, nf, L + gamma_s, order, nf, L, aem_list[0], alphaem_running ) ker = qed_s.dispatcher( order, @@ -328,7 +329,7 @@ def quad_ker( # scale var exponentiated is directly applied on gamma if sv_mode == sv.Modes.exponentiated: gamma_v = sv.exponentiated.gamma_variation_qed( - gamma_v, order, nf, L + gamma_v, order, nf, L, aem_list[0], alphaem_running ) ker = qed_v.dispatcher( order, @@ -352,7 +353,7 @@ def quad_ker( # scale var exponentiated is directly applied on gamma if sv_mode == sv.Modes.exponentiated: gamma_ns = sv.exponentiated.gamma_variation_qed( - gamma_ns, order, nf, L + gamma_ns, order, nf, L, aem_list[0], alphaem_running ) ker = qed_ns.dispatcher( order, @@ -361,6 +362,7 @@ def quad_ker( as1, as0, aem_list, + alphaem_running, nf, ev_op_iterations, ) @@ -477,6 +479,11 @@ def a_s(self): def a_em(self): """Return the computed values for :math:`a_{em}`.""" return (self.a[0][1], self.a[1][1]) + + @property + def alphaem_running(self): + """Return the value of alphaem_running""" + return self.managers["couplings"].alphaem_running @property def aem_list_as(self): @@ -587,6 +594,7 @@ def quad_ker(self, label, logx, areas): as1=self.a_s[1], as0=self.a_s[0], aem_list=self.aem_list_as, + alphaem_running=self.alphaem_running, nf=self.nf, L=np.log(self.fact_to_ren), ev_op_iterations=self.config["ev_op_iterations"], diff --git a/src/eko/kernels/QEDnon_singlet.py b/src/eko/kernels/QEDnon_singlet.py index 26b998ba4..291a956ed 100644 --- a/src/eko/kernels/QEDnon_singlet.py +++ b/src/eko/kernels/QEDnon_singlet.py @@ -233,7 +233,7 @@ def nnlo_aem2_exact(gamma_ns, a1, a0, aem, nf): @nb.njit(cache=True) def dispatcher( - order, method, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations + order, method, gamma_ns, a1, a0, aem_list, alphaem_running, nf, ev_op_iterations ): # pylint: disable=too-many-return-statements """ Determine used kernel and call it. @@ -271,6 +271,32 @@ def dispatcher( ) # this if is probably useless since when order[1] == 0 # the code never enters in this module + if not alphaem_running : + return fixed_alpha_solution(order, gamma_ns, a1, a0, aem_list, nf) + else : + return running_alpha_solution(order, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations) + +@nb.njit(cache=True) +def fixed_alpha_solution(order, gamma_ns, a1, a0, aem_list, nf): + aem = aem_list[0] + if order[1] == 1: + if order[0] == 1: + return lo_aem1_exact(gamma_ns, a1, a0, aem, nf) + if order[0] == 2: + return nlo_aem1_exact(gamma_ns, a1, a0, aem, nf) + if order[0] == 3: + return nnlo_aem1_exact(gamma_ns, a1, a0, aem, nf) + if order[1] == 2: + if order[0] == 1: + return lo_aem2_exact(gamma_ns, a1, a0, aem, nf) + if order[0] == 2: + return nlo_aem2_exact(gamma_ns, a1, a0, aem, nf) + if order[0] == 3: + return nnlo_aem2_exact(gamma_ns, a1, a0, aem, nf) + raise NotImplementedError("Selected order is not implemented") + +@nb.njit(cache=True) +def running_alpha_solution(order, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations): a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) res = 1. # For the moment implemented in this way to make numba compile it diff --git a/src/eko/scale_variations/exponentiated.py b/src/eko/scale_variations/exponentiated.py index 9988bfb7a..8e3db9aa3 100644 --- a/src/eko/scale_variations/exponentiated.py +++ b/src/eko/scale_variations/exponentiated.py @@ -47,7 +47,7 @@ def gamma_variation(gamma, order, nf, L): @nb.njit(cache=True) -def gamma_variation_qed(gamma, order, nf, L): +def gamma_variation_qed(gamma, order, nf, L, aem, alphaem_running): """ Adjust the anomalous dimensions with the scale variations. @@ -70,33 +70,52 @@ def gamma_variation_qed(gamma, order, nf, L): # TODO : Implement scale variations for fixed alpha_em # since we are modifying *in-place* be carefull, that the order matters! # and indeed, we need to adjust the high elements first - beta0qcd = beta.beta_qcd((2, 0), nf) - beta1qcd = beta.beta_qcd((3, 0), nf) - beta2qcd = beta.beta_qcd((4, 0), nf) - beta0qed = beta.beta_qed((0, 2), nf) - # beta1qed = beta.beta_qcd((0, 3), nf) - # beta01qcd = beta.beta_qcd((2, 1), nf) - if order[0] >= 4: - gamma[4, 0] -= ( - 3 * beta0qcd * L * gamma[3, 0] - + (2 * beta1qcd * L - 3 * beta0qcd**2 * L**2) * gamma[2, 0] - + ( - beta2qcd * L - - 5 / 2 * beta1qcd * beta0qcd * L**2 - + beta0qcd**3 * L**3 + if not alphaem_running : + beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qed((0,2),nf) + beta1 = beta.beta_qcd((3, 0), nf) + beta2 = beta.beta_qcd((4, 0), nf) + if order[0] >= 4: + gamma[4,0] -= ( + 3.0 * beta0 * L * gamma[3,0] + + (2.0 * beta1 * L - 3.0 * beta0**2 * L**2) * gamma[2,0] + + (beta2 * L - 5.0 / 2.0 * beta1 * beta0 * L**2 + beta0**3 * L**3) + * gamma[1,0] ) - * gamma[1, 0] - ) - if order[0] >= 3: - gamma[3, 0] -= ( - 2 * beta0qcd * gamma[2, 0] * L - + (beta1qcd * L - beta0qcd**2 * L**2) * gamma[1, 0] - ) - if order[0] >= 2: - gamma[2, 0] -= beta0qcd * gamma[1, 0] * L - # we are neglecting the order (2,1) - # if order[1] >= 1: - # gamma[2, 1] -= -L * (beta01qcd * gamma[1,0] + beta0qcd * gamma[1,1]) - if order[1] >= 2 : - gamma[0, 2] -= beta0qed * gamma[0, 1] * L - return gamma + if order[0] >= 3: + gamma[3,0] -= ( + 2.0 * beta0 * gamma[2,0] * L + (beta1 * L - beta0**2 * L**2) * gamma[1,0] + ) + if order[0] >= 2: + gamma[2,0] -= beta0 * gamma[1,0] * L + return gamma + else: + beta0qcd = beta.beta_qcd((2, 0), nf) + beta1qcd = beta.beta_qcd((3, 0), nf) + beta2qcd = beta.beta_qcd((4, 0), nf) + beta0qed = beta.beta_qed((0, 2), nf) + # beta1qed = beta.beta_qcd((0, 3), nf) + # beta01qcd = beta.beta_qcd((2, 1), nf) + if order[0] >= 4: + gamma[4, 0] -= ( + 3 * beta0qcd * L * gamma[3, 0] + + (2 * beta1qcd * L - 3 * beta0qcd**2 * L**2) * gamma[2, 0] + + ( + beta2qcd * L + - 5 / 2 * beta1qcd * beta0qcd * L**2 + + beta0qcd**3 * L**3 + ) + * gamma[1, 0] + ) + if order[0] >= 3: + gamma[3, 0] -= ( + 2 * beta0qcd * gamma[2, 0] * L + + (beta1qcd * L - beta0qcd**2 * L**2) * gamma[1, 0] + ) + if order[0] >= 2: + gamma[2, 0] -= beta0qcd * gamma[1, 0] * L + # we are neglecting the order (2,1) + # if order[1] >= 1: + # gamma[2, 1] -= -L * (beta01qcd * gamma[1,0] + beta0qcd * gamma[1,1]) + if order[1] >= 2 : + gamma[0, 2] -= beta0qed * gamma[0, 1] * L + return gamma diff --git a/tests/eko/test_couplings.py b/tests/eko/test_couplings.py index 154fe5f8e..4de25e879 100644 --- a/tests/eko/test_couplings.py +++ b/tests/eko/test_couplings.py @@ -593,7 +593,7 @@ def test_compute_aem_as(self): scale_ref = 91.2**2 thresh_setup = (2, 4, 175) scale_target = [5, 10, 50, 100, 150] # nf = 5 - for running_alphaem in [False]: + for alphaem_running in [False]: for qcd in range(1, 4 + 1): for qed in range(0, 2 + 1): couplings = Couplings( @@ -604,7 +604,7 @@ def test_compute_aem_as(self): (qcd, qed), "exact", nf_ref=5, - alphaem_running=running_alphaem, + alphaem_running=alphaem_running, ) a_values = [] for Qf in scale_target: @@ -614,7 +614,7 @@ def test_compute_aem_as(self): alphaem_ref / 4 / np.pi, alphas_ref / 4 / np.pi, a[0], nf=5 ) np.testing.assert_allclose(aem, a[1], atol=1e-10, rtol=1e-10) - for running_alphaem in [True, False]: + for alphaem_running in [True, False]: for qcd in range(1, 4 + 1): for qed in range(0, 0 + 1): couplings = Couplings( @@ -625,7 +625,7 @@ def test_compute_aem_as(self): (qcd, qed), "exact", nf_ref=5, - alphaem_running=running_alphaem, + alphaem_running=alphaem_running, ) a_values = [] for Qf in scale_target: @@ -635,7 +635,7 @@ def test_compute_aem_as(self): alphaem_ref / 4 / np.pi, alphas_ref / 4 / np.pi, a[0], nf=5 ) np.testing.assert_allclose(aem, a[1], atol=1e-10, rtol=1e-10) - for running_alphaem in [True]: + for alphaem_running in [True]: for qcd in range(1, 4 + 1): for qed in range(1, 2 + 1): couplings = Couplings( @@ -646,7 +646,7 @@ def test_compute_aem_as(self): (qcd, qed), "exact", nf_ref=5, - alphaem_running=running_alphaem, + alphaem_running=alphaem_running, ) a_values = [] for Qf in scale_target: @@ -657,7 +657,7 @@ def test_compute_aem_as(self): ) np.testing.assert_allclose(aem, a[1], atol=1e-6, rtol=1e-2) scale_target = [2.1, 2.5, 3.0, 3.5] # nf = 4 - for running_alphaem in [True]: + for alphaem_running in [True]: for qcd in range(1, 4 + 1): for qed in range(1, 2 + 1): couplings = Couplings( @@ -668,7 +668,7 @@ def test_compute_aem_as(self): (qcd, qed), "exact", nf_ref=5, - alphaem_running=running_alphaem, + alphaem_running=alphaem_running, ) a_ref = couplings.a(4.0**2, nf_to=4) a_values = [] diff --git a/tests/eko/test_ev_operator.py b/tests/eko/test_ev_operator.py index 79828efa8..23799ae81 100644 --- a/tests/eko/test_ev_operator.py +++ b/tests/eko/test_ev_operator.py @@ -44,6 +44,7 @@ def test_quad_ker(monkeypatch): as1=1, as0=2, aem_list=[0.00058], + alphaem_running=False, nf=3, L=0, ev_op_iterations=0, @@ -64,6 +65,7 @@ def test_quad_ker(monkeypatch): as1=1, as0=2, aem_list=[0.00058], + alphaem_running=False, nf=3, L=0, ev_op_iterations=0, @@ -84,6 +86,7 @@ def test_quad_ker(monkeypatch): as1=1, as0=2, aem_list=[0.00058], + alphaem_running=False, nf=3, L=0, ev_op_iterations=0, @@ -104,6 +107,7 @@ def test_quad_ker(monkeypatch): as1=1, as0=2, aem_list=[0.00058], + alphaem_running=False, nf=3, L=0, ev_op_iterations=0, @@ -124,6 +128,7 @@ def test_quad_ker(monkeypatch): as1=1, as0=2, aem_list=[0.00058], + alphaem_running=False, nf=3, L=0, ev_op_iterations=0, @@ -144,6 +149,7 @@ def test_quad_ker(monkeypatch): as1=1, as0=2, aem_list=[0.00058], + alphaem_running=False, nf=3, L=0, ev_op_iterations=0, @@ -164,6 +170,7 @@ def test_quad_ker(monkeypatch): as1=1, as0=2, aem_list=[0.00058], + alphaem_running=False, nf=3, L=0, ev_op_iterations=0, @@ -184,6 +191,7 @@ def test_quad_ker(monkeypatch): as1=1, as0=2, aem_list=[0.00058], + alphaem_running=False, nf=3, L=0, ev_op_iterations=0, @@ -206,6 +214,7 @@ def test_quad_ker(monkeypatch): as1=1, as0=2, aem_list=[0.00058], + alphaem_running=False, nf=3, L=0, ev_op_iterations=0, @@ -236,6 +245,7 @@ def test_quad_ker(monkeypatch): as1=1, as0=2, aem_list=[0.00058], + alphaem_running=False, nf=3, L=0, ev_op_iterations=0, @@ -258,6 +268,7 @@ def test_quad_ker(monkeypatch): as1=1, as0=2, aem_list=[0.00058], + alphaem_running=False, nf=3, L=0, ev_op_iterations=0, @@ -639,7 +650,8 @@ def quad_ker_pegasus( bf.areas_representation, a1, a0, - 0.00058, + [0.00058], + False, nf, L, ev_op_iterations, diff --git a/tests/eko/test_kernels_QEDns.py b/tests/eko/test_kernels_QEDns.py index a24ed0f6c..9178526ba 100644 --- a/tests/eko/test_kernels_QEDns.py +++ b/tests/eko/test_kernels_QEDns.py @@ -34,7 +34,7 @@ def test_zero(): for method in methods: np.testing.assert_allclose( ns.dispatcher( - order, method, gamma_ns, 1.0, 1.0, [1.0, 1.0], nf, ev_op_iterations + order, method, gamma_ns, 1.0, 1.0, [1.0, 1.0], False, nf, ev_op_iterations ), 1.0, ) @@ -46,6 +46,7 @@ def test_zero(): 2.0, 1.0, [1.0, 1.0], + False, nf, ev_op_iterations, ), @@ -68,7 +69,7 @@ def test_zero_true_gamma(): for method in methods: np.testing.assert_allclose( ns.dispatcher( - order, method, gamma_ns, 1.0, 1.0, [1.0, 1.0], nf, ev_op_iterations + order, method, gamma_ns, 1.0, 1.0, [1.0, 1.0], False, nf, ev_op_iterations ), 1.0, ) @@ -80,6 +81,7 @@ def test_zero_true_gamma(): 2.0, 1.0, [1.0, 1.0], + False, nf, ev_op_iterations, ), @@ -120,7 +122,7 @@ def test_ode(): r = gammatot / betatot for method in methods: rhs = r * ns.dispatcher( - order, method, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations + order, method, gamma_ns, a1, a0, aem_list, False, nf, ev_op_iterations ) lhs = ( ns.dispatcher( @@ -130,6 +132,7 @@ def test_ode(): a1 + 0.5 * delta_a, a0, aem_list, + False, nf, ev_op_iterations, ) @@ -140,6 +143,7 @@ def test_ode(): a1 - 0.5 * delta_a, a0, aem_list, + False, nf, ev_op_iterations, ) @@ -176,7 +180,7 @@ def test_ode_true_gamma(): r = gammatot / betatot for method in methods: rhs = r * ns.dispatcher( - order, method, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations + order, method, gamma_ns, a1, a0, aem_list, False, nf, ev_op_iterations ) lhs = ( ns.dispatcher( @@ -186,6 +190,7 @@ def test_ode_true_gamma(): a1 + 0.5 * delta_a, a0, aem_list, + False, nf, ev_op_iterations, ) @@ -196,6 +201,7 @@ def test_ode_true_gamma(): a1 - 0.5 * delta_a, a0, aem_list, + False, nf, ev_op_iterations, ) @@ -206,5 +212,5 @@ def test_ode_true_gamma(): def test_error(): with pytest.raises(NotImplementedError): ns.dispatcher( - (4, 2), "iterate-exact", np.random.rand(4, 3), 0.2, 0.1, [0.01], 3, 10 + (4, 2), "iterate-exact", np.random.rand(4, 3), 0.2, 0.1, [0.01], False, 3, 10 ) From 93e6eb98dec48cb62a640b38652ad8b77e303868 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 20 Oct 2022 11:30:00 +0200 Subject: [PATCH 205/312] Add documentation for fixed_alphaem_exact running_alphaem_exact --- src/eko/evolution_operator/__init__.py | 1 - src/eko/kernels/QEDnon_singlet.py | 64 +++++++++++++++++++++++--- 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index fe1ed771c..0bf8628e6 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -301,7 +301,6 @@ def quad_ker( gamma_s = ad.gamma_singlet_qed(order, ker_base.n, nf) # scale var exponentiated is directly applied on gamma if sv_mode == sv.Modes.exponentiated: - # the aem terms do not get scale variations # TODO : double check it gamma_s = sv.exponentiated.gamma_variation_qed( gamma_s, order, nf, L, aem_list[0], alphaem_running diff --git a/src/eko/kernels/QEDnon_singlet.py b/src/eko/kernels/QEDnon_singlet.py index 291a956ed..bc67687ba 100644 --- a/src/eko/kernels/QEDnon_singlet.py +++ b/src/eko/kernels/QEDnon_singlet.py @@ -252,8 +252,10 @@ def dispatcher( target coupling value a0 : float initial coupling value - aem : float - electromagnetic coupling value + aem_list : numpy.ndarray + electromagnetic coupling values + alphaem_running : Bool + running of alphaem nf : int number of active flavors ev_op_iterations : int @@ -272,13 +274,36 @@ def dispatcher( # this if is probably useless since when order[1] == 0 # the code never enters in this module if not alphaem_running : - return fixed_alpha_solution(order, gamma_ns, a1, a0, aem_list, nf) + aem = aem_list[0] + return fixed_alphaem_exact(order, gamma_ns, a1, a0, aem, nf) else : - return running_alpha_solution(order, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations) + return running_alphaem_exact(order, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations) @nb.njit(cache=True) -def fixed_alpha_solution(order, gamma_ns, a1, a0, aem_list, nf): - aem = aem_list[0] +def fixed_alphaem_exact(order, gamma_ns, a1, a0, aem, nf): + """ + Compute exact solution for fixed alphaem. + + Parameters + ---------- + order : tuple(int,int) + perturbation order + gamma_ns : numpy.ndarray + non-singlet anomalous dimensions + a1 : float + target coupling value + a0 : float + initial coupling value + aem : float + electromagnetic coupling value + nf : int + number of active flavors + + Returns + ------- + e_ns : complex + non-singlet EKO + """ if order[1] == 1: if order[0] == 1: return lo_aem1_exact(gamma_ns, a1, a0, aem, nf) @@ -296,7 +321,32 @@ def fixed_alpha_solution(order, gamma_ns, a1, a0, aem_list, nf): raise NotImplementedError("Selected order is not implemented") @nb.njit(cache=True) -def running_alpha_solution(order, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations): +def running_alphaem_exact(order, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations): + """ + Compute exact solution for running alphaem. + + Parameters + ---------- + order : tuple(int,int) + perturbation order + gamma_ns : numpy.ndarray + non-singlet anomalous dimensions + a1 : float + target coupling value + a0 : float + initial coupling value + aem_list : numpy.ndarray + electromagnetic coupling values + nf : int + number of active flavors + ev_op_iterations : int + number of evolution steps + + Returns + ------- + e_ns : complex + non-singlet EKO + """ a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) res = 1. # For the moment implemented in this way to make numba compile it From 7f537e1cc9787ba87b248f821685089fbb797fc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 20 Oct 2022 15:03:50 +0200 Subject: [PATCH 206/312] Implement expanded scale variations for QED --- src/eko/evolution_operator/__init__.py | 32 ++--- src/eko/kernels/QEDnon_singlet.py | 41 ------ src/eko/scale_variations/expanded.py | 165 ++++++++++++++++++++++ src/eko/scale_variations/exponentiated.py | 24 +--- tests/eko/test_ev_operator.py | 1 + 5 files changed, 187 insertions(+), 76 deletions(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 0bf8628e6..e38cc3eee 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -303,7 +303,7 @@ def quad_ker( if sv_mode == sv.Modes.exponentiated: # TODO : double check it gamma_s = sv.exponentiated.gamma_variation_qed( - gamma_s, order, nf, L, aem_list[0], alphaem_running + gamma_s, order, nf, L, alphaem_running ) ker = qed_s.dispatcher( order, @@ -318,17 +318,17 @@ def quad_ker( ) # scale var expanded is applied on the kernel # TODO : check expanded scale variations - # if sv_mode == sv.Modes.expanded and not is_threshold: - # ker = np.ascontiguousarray(ker) @ np.ascontiguousarray( - # sv.expanded.singlet_variation(gamma_s, as1, order, nf, L) - # ) + if sv_mode == sv.Modes.expanded and not is_threshold: + ker = np.ascontiguousarray(ker) @ np.ascontiguousarray( + sv.expanded.QEDsinglet_variation(gamma_s, as1, aem_list[-1], alphaem_running, order, nf, L) + ) ker = select_QEDsinglet_element(ker, mode0, mode1) elif ker_base.is_QEDvalence: gamma_v = ad.gamma_valence_qed(order, ker_base.n, nf) # scale var exponentiated is directly applied on gamma if sv_mode == sv.Modes.exponentiated: gamma_v = sv.exponentiated.gamma_variation_qed( - gamma_v, order, nf, L, aem_list[0], alphaem_running + gamma_v, order, nf, L, alphaem_running ) ker = qed_v.dispatcher( order, @@ -342,17 +342,17 @@ def quad_ker( ev_op_max_order, ) # scale var expanded is applied on the kernel - # if sv_mode == sv.Modes.expanded and not is_threshold: - # ker = np.ascontiguousarray( - # sv.expanded.singlet_variation(gamma_v, as1, order, nf, L) - # ) @ np.ascontiguousarray(ker) + if sv_mode == sv.Modes.expanded and not is_threshold: + ker = np.ascontiguousarray( + sv.expanded.QEDvalence_variation(gamma_v, as1, aem_list[-1], alphaem_running, order, nf, L) + ) @ np.ascontiguousarray(ker) ker = select_QEDvalence_element(ker, mode0, mode1) else: gamma_ns = ad.gamma_ns_qed(order, mode0, ker_base.n, nf) # scale var exponentiated is directly applied on gamma if sv_mode == sv.Modes.exponentiated: gamma_ns = sv.exponentiated.gamma_variation_qed( - gamma_ns, order, nf, L, aem_list[0], alphaem_running + gamma_ns, order, nf, L, alphaem_running ) ker = qed_ns.dispatcher( order, @@ -365,10 +365,10 @@ def quad_ker( nf, ev_op_iterations, ) - # if sv_mode == sv.Modes.expanded and not is_threshold: - # ker = ( - # sv.expanded.non_singlet_variation(gamma_ns, as1, order, nf, L) * ker - # ) + if sv_mode == sv.Modes.expanded and not is_threshold: + ker = ( + sv.expanded.QEDnon_singlet_variation(gamma_ns, as1, aem_list[-1], alphaem_running, order, nf, L) * ker + ) # recombine everything return np.real(ker * integrand) @@ -789,7 +789,7 @@ def copy_ns_ops(self): ].error.copy() # at O(as0aem1) u-=u+, d-=d+ # starting from O(as1aem1) P+ != P- - # However the solution with pure QED are not implemented in EKO + # However the solution with pure QED is not implemented in EKO # so the ns anomalous dimensions are always different # elif self.order[1] == 1 and self.order[0] == 0: # self.op_members[ diff --git a/src/eko/kernels/QEDnon_singlet.py b/src/eko/kernels/QEDnon_singlet.py index bc67687ba..dbfcc6ede 100644 --- a/src/eko/kernels/QEDnon_singlet.py +++ b/src/eko/kernels/QEDnon_singlet.py @@ -190,47 +190,6 @@ def nnlo_aem2_exact(gamma_ns, a1, a0, aem, nf): ) -# @nb.njit(cache=True) -# def solution_running_alpha(func, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations): -# """ -# Compute non-singlet EKO with running alphaem. - -# Parameters -# ---------- -# func: function -# solution for fixed alphaem -# gamma_ns : numpy.ndarray -# non-singlet anomalous dimensions -# a1 : float -# target coupling value -# a0 : float -# initial coupling value -# aem_list : numpy.ndarray -# electromagnetic coupling values -# nf : int -# number of active flavors -# ev_op_iterations : int -# number of evolution steps - -# Returns -# ------- -# e_ns : complex -# non-singlet EKO - -# """ -# a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) -# # res = np.prod( -# # [ -# # func(gamma_ns, ah, a_steps[step], aem_list[step], nf) -# # for step, ah in enumerate(a_steps[1:]) -# # ] -# # ) -# res = 1. -# for step, ah in enumerate(a_steps[1:]): -# res *= func(gamma_ns, ah, a_steps[step], aem_list[step], nf) -# return res - - @nb.njit(cache=True) def dispatcher( order, method, gamma_ns, a1, a0, aem_list, alphaem_running, nf, ev_op_iterations diff --git a/src/eko/scale_variations/expanded.py b/src/eko/scale_variations/expanded.py index aeea77b76..bb4f2f6f6 100644 --- a/src/eko/scale_variations/expanded.py +++ b/src/eko/scale_variations/expanded.py @@ -171,3 +171,168 @@ def singlet_variation(gamma, a_s, order, nf, L): gamma, L, beta0, beta1, gamma0e2, gamma0e3, g1g0, g0g1 ) return sv_ker + +@nb.njit(cache=True) +def QEDnon_singlet_variation(gamma, a_s, a_em, alphaem_running, order, nf, L): + """Non-singlet scale variation dispatcher. + + Parameters + ---------- + gamma : numpy.ndarray + anomalous dimensions + a_s : float + target coupling value + order : int + perturbation order + nf : int + number of active flavors + L : float + logarithmic ratio of factorization and renormalization scale + + Returns + ------- + complex + scale variation kernel + """ + if not alphaem_running : + return non_singlet_variation(gamma[1:, 0], a_s, order, nf, L) + else : + sv_ker = 1.0 + if order[0] >= 2: + sv_ker += a_s * variation_as1(gamma[1:, 0], L) + if order[1] >= 2: + sv_ker += a_em * variation_as1(gamma[0, 1:], L) + if order[0] >= 3: + beta0 = beta.beta_qcd_as2(nf) + sv_ker += a_s**2 * variation_as2(gamma[1:, 0], L, beta0, gamma[1,0] ** 2) + if order[0] >= 4: + beta1 = beta.beta_qcd((3, 0), nf) + g0g1 = gamma[1,0] * gamma[2,0] + sv_ker += a_s**3 * variation_as3( + gamma[1:, 0], L, beta0, beta1, gamma[1,0] ** 2, gamma[1,0] ** 3, g0g1, g0g1 + ) + return sv_ker + +@nb.njit(cache=True) +def QEDsinglet_variation(gamma, a_s, a_em, alphaem_running, order, nf, L): + """Singlet scale variation dispatcher. + + Parameters + ---------- + gamma : numpy.ndarray + anomalous dimensions + a_s : float + target coupling value + order : int + perturbation order + nf : int + number of active flavors + L : float + logarithmic ratio of factorization and renormalization scale + + Returns + ------- + numpy.ndarray + scale variation kernel + """ + sv_ker = np.eye(4, dtype=np.complex_) + gamma = np.ascontiguousarray(gamma) + if not alphaem_running : + if order[0] >= 2: + sv_ker += a_s * variation_as1(gamma[1:, 0], L) + if order[0] >= 3: + beta0 = beta.beta_qcd_as2(nf) + gamma10e2 = gamma[1, 0] @ gamma[1, 0] + sv_ker += a_s**2 * variation_as2(gamma[1:, 0], L, beta0, gamma10e2) + if order[0] >= 4: + beta1 = beta.beta_qcd((3, 0), nf) + gamma10e3 = gamma10e2 @ gamma[1,0] + # here the product is not commutative + g20g10 = gamma[2,0] @ gamma[1,0] + g10g20 = gamma[1,0] @ gamma[2,0] + sv_ker += a_s**3 * variation_as3( + gamma[1:, 0], L, beta0, beta1, gamma10e2, gamma10e3, g20g10, g10g20 + ) + else : + if order[0] >= 2: + sv_ker += a_s * variation_as1(gamma[1:, 0], L) + if order[1] >= 2: + sv_ker += a_em * variation_as1(gamma[0, 1:], L) + if order[0] >= 3: + beta0 = beta.beta_qcd_as2(nf) + gamma10e2 = gamma[1, 0] @ gamma[1, 0] + sv_ker += a_s**2 * variation_as2(gamma[1:, 0], L, beta0, gamma10e2) + if order[0] >= 4: + beta1 = beta.beta_qcd((3, 0), nf) + gamma10e3 = gamma10e2 @ gamma[1,0] + # here the product is not commutative + g20g10 = gamma[2,0] @ gamma[1,0] + g10g20 = gamma[1,0] @ gamma[2,0] + sv_ker += a_s**3 * variation_as3( + gamma[1:, 0], L, beta0, beta1, gamma10e2, gamma10e3, g20g10, g10g20 + ) + return sv_ker + +@nb.njit(cache=True) +def QEDvalence_variation(gamma, a_s, a_em, alphaem_running, order, nf, L): + """Singlet scale variation dispatcher. + + Parameters + ---------- + gamma : numpy.ndarray + anomalous dimensions + a_s : float + target coupling value + order : int + perturbation order + nf : int + number of active flavors + L : float + logarithmic ratio of factorization and renormalization scale + + Returns + ------- + numpy.ndarray + scale variation kernel + """ + sv_ker = np.eye(2, dtype=np.complex_) + gamma = np.ascontiguousarray(gamma) + if not alphaem_running : + sv_ker = np.eye(2, dtype=np.complex_) + gamma = np.ascontiguousarray(gamma) + if order[0] >= 2: + sv_ker += a_s * variation_as1(gamma[1:, 0], L) + if order[0] >= 3: + beta0 = beta.beta_qcd_as2(nf) + gamma10e2 = gamma[1, 0] @ gamma[1, 0] + sv_ker += a_s**2 * variation_as2(gamma[1:, 0], L, beta0, gamma10e2) + if order[0] >= 4: + beta1 = beta.beta_qcd((3, 0), nf) + gamma10e3 = gamma10e2 @ gamma[1,0] + # here the product is not commutative + g20g10 = gamma[2,0] @ gamma[1,0] + g10g20 = gamma[1,0] @ gamma[2,0] + sv_ker += a_s**3 * variation_as3( + gamma[1:, 0], L, beta0, beta1, gamma10e2, gamma10e3, g20g10, g10g20 + ) + else : + sv_ker = np.eye(2, dtype=np.complex_) + gamma = np.ascontiguousarray(gamma) + if order[0] >= 2: + sv_ker += a_s * variation_as1(gamma[1:, 0], L) + if order[1] >= 2: + sv_ker += a_em * variation_as1(gamma[0, 1:], L) + if order[0] >= 3: + beta0 = beta.beta_qcd_as2(nf) + gamma10e2 = gamma[1, 0] @ gamma[1, 0] + sv_ker += a_s**2 * variation_as2(gamma[1:, 0], L, beta0, gamma10e2) + if order[0] >= 4: + beta1 = beta.beta_qcd((3, 0), nf) + gamma10e3 = gamma10e2 @ gamma[1,0] + # here the product is not commutative + g20g10 = gamma[2,0] @ gamma[1,0] + g10g20 = gamma[1,0] @ gamma[2,0] + sv_ker += a_s**3 * variation_as3( + gamma[1:, 0], L, beta0, beta1, gamma10e2, gamma10e3, g20g10, g10g20 + ) + return sv_ker diff --git a/src/eko/scale_variations/exponentiated.py b/src/eko/scale_variations/exponentiated.py index 8e3db9aa3..71f883987 100644 --- a/src/eko/scale_variations/exponentiated.py +++ b/src/eko/scale_variations/exponentiated.py @@ -47,7 +47,7 @@ def gamma_variation(gamma, order, nf, L): @nb.njit(cache=True) -def gamma_variation_qed(gamma, order, nf, L, aem, alphaem_running): +def gamma_variation_qed(gamma, order, nf, L, alphaem_running): """ Adjust the anomalous dimensions with the scale variations. @@ -67,27 +67,13 @@ def gamma_variation_qed(gamma, order, nf, L, aem, alphaem_running): gamma : numpy.ndarray adjusted anomalous dimensions """ - # TODO : Implement scale variations for fixed alpha_em # since we are modifying *in-place* be carefull, that the order matters! # and indeed, we need to adjust the high elements first if not alphaem_running : - beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qed((0,2),nf) - beta1 = beta.beta_qcd((3, 0), nf) - beta2 = beta.beta_qcd((4, 0), nf) - if order[0] >= 4: - gamma[4,0] -= ( - 3.0 * beta0 * L * gamma[3,0] - + (2.0 * beta1 * L - 3.0 * beta0**2 * L**2) * gamma[2,0] - + (beta2 * L - 5.0 / 2.0 * beta1 * beta0 * L**2 + beta0**3 * L**3) - * gamma[1,0] - ) - if order[0] >= 3: - gamma[3,0] -= ( - 2.0 * beta0 * gamma[2,0] * L + (beta1 * L - beta0**2 * L**2) * gamma[1,0] - ) - if order[0] >= 2: - gamma[2,0] -= beta0 * gamma[1,0] * L - return gamma + # if alphaem is fixed then only alphas is varied so gamma[0,1] and gamma[0,2] + # don't get a variation while gamma[1,1] gets a variation that is O(as2aem1) + # that we are neglecting + return gamma_variation(gamma[1:, 0], order, nf, L) else: beta0qcd = beta.beta_qcd((2, 0), nf) beta1qcd = beta.beta_qcd((3, 0), nf) diff --git a/tests/eko/test_ev_operator.py b/tests/eko/test_ev_operator.py index 23799ae81..447fc61a7 100644 --- a/tests/eko/test_ev_operator.py +++ b/tests/eko/test_ev_operator.py @@ -3,6 +3,7 @@ import os import numpy as np +import pytest import scipy.integrate from eko import anomalous_dimensions as ad From 1dd28e1ff7f36f9d2d17a633d58edb10072e1023 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 20 Oct 2022 15:47:56 +0200 Subject: [PATCH 207/312] Correct typo in exponentiated.py --- src/eko/scale_variations/exponentiated.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/eko/scale_variations/exponentiated.py b/src/eko/scale_variations/exponentiated.py index 71f883987..0449da6a3 100644 --- a/src/eko/scale_variations/exponentiated.py +++ b/src/eko/scale_variations/exponentiated.py @@ -73,7 +73,8 @@ def gamma_variation_qed(gamma, order, nf, L, alphaem_running): # if alphaem is fixed then only alphas is varied so gamma[0,1] and gamma[0,2] # don't get a variation while gamma[1,1] gets a variation that is O(as2aem1) # that we are neglecting - return gamma_variation(gamma[1:, 0], order, nf, L) + gamma[1:, 0] = gamma_variation(gamma[1:, 0], order, nf, L) + return gamma else: beta0qcd = beta.beta_qcd((2, 0), nf) beta1qcd = beta.beta_qcd((3, 0), nf) From e65f0238add6c574be6aa274bcb7bcf6f8610609 Mon Sep 17 00:00:00 2001 From: niclaurenti Date: Thu, 20 Oct 2022 16:05:21 +0200 Subject: [PATCH 208/312] Implement benchmark_sv in apfel-bench --- benchmarks/apfel_bench.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/benchmarks/apfel_bench.py b/benchmarks/apfel_bench.py index 9b6a1d646..4bc5c2397 100644 --- a/benchmarks/apfel_bench.py +++ b/benchmarks/apfel_bench.py @@ -229,6 +229,42 @@ def benchmark_plain(self, pto, qed): ["NNPDF31_nnlo_as_0118_luxqed"], ) + def benchmark_sv(self, pto, qed, svmode): + """Scale Variation""" + + ts = [] + th = self.ffns_theory.copy() + th.update( + { + "PTO": [pto], + "QED": [qed], + "XIR": [np.sqrt(0.5)], + "fact_to_ren_scale_ratio": [np.sqrt(2.0)], + "ModSV": [svmode], + "EScaleVar": [0], + } + ) + ts.extend(cartesian_product(th)) + th = self.ffns_theory.copy() + th.update( + { + "PTO": [pto], + "QED": [qed], + "XIR": [np.sqrt(2.0)], + "fact_to_ren_scale_ratio": [np.sqrt(0.5)], + "ModSV": [svmode], + "EScaleVar": [0], + } + ) + ts.extend(cartesian_product(th)) + self.skip_pdfs = lambda _theory: [ + -6, + 6, + "Tu8", + "Vu8", + ] + self.run(ts, operators.build(operators.apfel_config), ["ToyLH"]) + class BenchmarkVFNS_qed(ApfelBenchmark): """Benckmark FFNS""" From b5a7c9e07f7b5cfed023b17ed3bc2e0958193f05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 21 Oct 2022 15:05:19 +0200 Subject: [PATCH 209/312] Test sv exponentiated for qed --- benchmarks/NNPDF_bench.py | 6 ++- src/eko/anomalous_dimensions/as1aem1.py | 4 +- src/eko/beta.py | 6 +-- src/eko/couplings.py | 8 +-- src/eko/evolution_operator/__init__.py | 16 ++++-- src/eko/kernels/QEDnon_singlet.py | 12 +++-- src/eko/kernels/QEDsinglet.py | 4 +- src/eko/kernels/QEDvalence.py | 4 +- src/eko/output/manipulate.py | 45 ++++++++--------- src/eko/scale_variations/expanded.py | 52 ++++++++++++-------- src/eko/scale_variations/exponentiated.py | 6 +-- tests/eko/test_beta.py | 4 +- tests/eko/test_couplings.py | 35 ++++++++++--- tests/eko/test_ev_operator.py | 14 ++++-- tests/eko/test_kernels_QEDns.py | 60 +++++++++++++++++++---- tests/eko/test_kernels_QEDsinglet.py | 8 +-- tests/eko/test_kernels_QEDvalence.py | 8 +-- tests/eko/test_sv_exponentiated.py | 22 ++++++++- 18 files changed, 216 insertions(+), 98 deletions(-) diff --git a/benchmarks/NNPDF_bench.py b/benchmarks/NNPDF_bench.py index 3793443eb..1d686d452 100644 --- a/benchmarks/NNPDF_bench.py +++ b/benchmarks/NNPDF_bench.py @@ -87,7 +87,11 @@ def benchmark_nnlo(self, Q0=1.65, Q2grid=(100,)): "Vu8", ] - operator_card = {**base_operator, "Q2grid": list(Q2grid), "ev_op_iterations": 10} + operator_card = { + **base_operator, + "Q2grid": list(Q2grid), + "ev_op_iterations": 10, + } self.run([theory_card], [operator_card], ["NNPDF31_nnlo_as_0118_luxqed"]) diff --git a/src/eko/anomalous_dimensions/as1aem1.py b/src/eko/anomalous_dimensions/as1aem1.py index 53fa74be3..ad3a97193 100644 --- a/src/eko/anomalous_dimensions/as1aem1.py +++ b/src/eko/anomalous_dimensions/as1aem1.py @@ -174,7 +174,9 @@ def gamma_qg(N, nf, sx): :math:`\\gamma_{qg}^{(1,1)}(N)` """ - return constants.TR / constants.CF / constants.CA * constants.NC * gamma_qph(N, nf, sx) + return ( + constants.TR / constants.CF / constants.CA * constants.NC * gamma_qph(N, nf, sx) + ) @nb.njit(cache=True) diff --git a/src/eko/beta.py b/src/eko/beta.py index 219fbc3dc..d721002aa 100644 --- a/src/eko/beta.py +++ b/src/eko/beta.py @@ -53,7 +53,7 @@ def beta_qed_aem2(nf): nd = nf - nu nl = 3 # TODO : pass nl as an argument?? beta_qed_aem2 = ( - -4.0 / 3 * (nl + constants.NC * (nu * constants.eu2 + nd * constants.ed2) ) + -4.0 / 3 * (nl + constants.NC * (nu * constants.eu2 + nd * constants.ed2)) ) return beta_qed_aem2 @@ -103,8 +103,8 @@ def beta_qed_aem3(nf): nu = constants.uplike_flavors(nf) nd = nf - nu nl = 3 # TODO : pass nl as an argument?? - beta_qed_aem3 = ( - -4.0 * (nl + constants.NC * ( nu * constants.eu2**2 + nd * constants.ed2**2) ) + beta_qed_aem3 = -4.0 * ( + nl + constants.NC * (nu * constants.eu2**2 + nd * constants.ed2**2) ) return beta_qed_aem3 diff --git a/src/eko/couplings.py b/src/eko/couplings.py index 5a7a0003d..e1906bb57 100644 --- a/src/eko/couplings.py +++ b/src/eko/couplings.py @@ -550,9 +550,9 @@ def unidimensional_exact(self, beta0, b_vec, u, a_ref, method, rtol): float coupling at target scale :math:`a(Q^2)` """ - if len(b_vec) == 1 : + if len(b_vec) == 1: return exact_lo(a_ref, beta0, u) - + def rge(_t, a, b_vec): rge = -(a**2) * (np.sum([a**k * b for k, b in enumerate(b_vec)])) return rge @@ -699,7 +699,7 @@ def compute_exact_fixed_alphaem(self, a_ref, nf, scale_from, scale_to): rtol=1e-6, ) return np.array([rge_qcd, a_ref[1]]) - + # def compute_exact_decoupled_running(self, a_ref, nf, scale_qcd_from, scale_qed_from, scale_to): # """Compute couplings via |RGE| with running alphaem without the mixed terms. @@ -829,7 +829,7 @@ def rge(_as, a_em, beta_qcd_vec, beta_qcd_mix, beta_qed_vec, beta_qed_mix): rtol=1e-6, ) return res.y[0][-1] - + # def compute_aem_as_decoupled(self, aem_ref, as_from, as_to, nf): # """Compute :math:`a_{em}` as a function of :math:`a_s` with no mixing terms. diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index e38cc3eee..418e7afae 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -301,7 +301,6 @@ def quad_ker( gamma_s = ad.gamma_singlet_qed(order, ker_base.n, nf) # scale var exponentiated is directly applied on gamma if sv_mode == sv.Modes.exponentiated: - # TODO : double check it gamma_s = sv.exponentiated.gamma_variation_qed( gamma_s, order, nf, L, alphaem_running ) @@ -320,7 +319,9 @@ def quad_ker( # TODO : check expanded scale variations if sv_mode == sv.Modes.expanded and not is_threshold: ker = np.ascontiguousarray(ker) @ np.ascontiguousarray( - sv.expanded.QEDsinglet_variation(gamma_s, as1, aem_list[-1], alphaem_running, order, nf, L) + sv.expanded.QEDsinglet_variation( + gamma_s, as1, aem_list[-1], alphaem_running, order, nf, L + ) ) ker = select_QEDsinglet_element(ker, mode0, mode1) elif ker_base.is_QEDvalence: @@ -344,7 +345,9 @@ def quad_ker( # scale var expanded is applied on the kernel if sv_mode == sv.Modes.expanded and not is_threshold: ker = np.ascontiguousarray( - sv.expanded.QEDvalence_variation(gamma_v, as1, aem_list[-1], alphaem_running, order, nf, L) + sv.expanded.QEDvalence_variation( + gamma_v, as1, aem_list[-1], alphaem_running, order, nf, L + ) ) @ np.ascontiguousarray(ker) ker = select_QEDvalence_element(ker, mode0, mode1) else: @@ -367,7 +370,10 @@ def quad_ker( ) if sv_mode == sv.Modes.expanded and not is_threshold: ker = ( - sv.expanded.QEDnon_singlet_variation(gamma_ns, as1, aem_list[-1], alphaem_running, order, nf, L) * ker + sv.expanded.QEDnon_singlet_variation( + gamma_ns, as1, aem_list[-1], alphaem_running, order, nf, L + ) + * ker ) # recombine everything @@ -478,7 +484,7 @@ def a_s(self): def a_em(self): """Return the computed values for :math:`a_{em}`.""" return (self.a[0][1], self.a[1][1]) - + @property def alphaem_running(self): """Return the value of alphaem_running""" diff --git a/src/eko/kernels/QEDnon_singlet.py b/src/eko/kernels/QEDnon_singlet.py index dbfcc6ede..9a864e534 100644 --- a/src/eko/kernels/QEDnon_singlet.py +++ b/src/eko/kernels/QEDnon_singlet.py @@ -232,11 +232,14 @@ def dispatcher( ) # this if is probably useless since when order[1] == 0 # the code never enters in this module - if not alphaem_running : + if not alphaem_running: aem = aem_list[0] return fixed_alphaem_exact(order, gamma_ns, a1, a0, aem, nf) - else : - return running_alphaem_exact(order, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations) + else: + return running_alphaem_exact( + order, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations + ) + @nb.njit(cache=True) def fixed_alphaem_exact(order, gamma_ns, a1, a0, aem, nf): @@ -279,6 +282,7 @@ def fixed_alphaem_exact(order, gamma_ns, a1, a0, aem, nf): return nnlo_aem2_exact(gamma_ns, a1, a0, aem, nf) raise NotImplementedError("Selected order is not implemented") + @nb.njit(cache=True) def running_alphaem_exact(order, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations): """ @@ -307,7 +311,7 @@ def running_alphaem_exact(order, gamma_ns, a1, a0, aem_list, nf, ev_op_iteration non-singlet EKO """ a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) - res = 1. + res = 1.0 # For the moment implemented in this way to make numba compile it # TODO : implement it with np.prod in a way that numba compiles it if order[1] == 1: diff --git a/src/eko/kernels/QEDsinglet.py b/src/eko/kernels/QEDsinglet.py index 07705d7b7..abec1db37 100644 --- a/src/eko/kernels/QEDsinglet.py +++ b/src/eko/kernels/QEDsinglet.py @@ -48,7 +48,9 @@ def eko_iterate(gamma_singlet, a1, a0, aem_list, nf, order, ev_op_iterations): betatot = 0 for i in range(0, order[0] + 1): for j in range(0, order[1] + 1): - betatot += a_half**1 * betaQCD[i, j] * a_half**i * aem_list[step] ** j + betatot += ( + a_half**1 * betaQCD[i, j] * a_half**i * aem_list[step] ** j + ) gamma += gamma_singlet[i, j] * a_half**i * aem_list[step] ** j ln = gamma / betatot * delta_a ek = np.ascontiguousarray(ad.exp_matrix(ln)[0]) diff --git a/src/eko/kernels/QEDvalence.py b/src/eko/kernels/QEDvalence.py index 0d6126f30..52a5c7c8d 100644 --- a/src/eko/kernels/QEDvalence.py +++ b/src/eko/kernels/QEDvalence.py @@ -49,7 +49,9 @@ def eko_iterate(gamma_valence, a1, a0, aem_list, nf, order, ev_op_iterations): betatot = 0 for i in range(0, order[0] + 1): for j in range(0, order[1] + 1): - betatot += a_half**1 * betaQCD[i, j] * a_half**i * aem_list[step] ** j + betatot += ( + a_half**1 * betaQCD[i, j] * a_half**i * aem_list[step] ** j + ) gamma += gamma_valence[i, j] * a_half**i * aem_list[step] ** j ln = gamma / betatot * delta_a ek = np.ascontiguousarray(ad.exp_matrix(ln)[0]) diff --git a/src/eko/output/manipulate.py b/src/eko/output/manipulate.py index 911a37734..f87cd8a7a 100644 --- a/src/eko/output/manipulate.py +++ b/src/eko/output/manipulate.py @@ -178,26 +178,27 @@ def to_evol(eko: EKO, source: bool = True, target: bool = False): eko.rotations._inputpids = br.evol_basis_pids if target: eko.rotations._targetpids = br.evol_basis_pids - + + def to_uni_evol(eko: EKO, source: bool = True, target: bool = False): - """ - Rotate the operator into unified evolution basis. - - This also assigns also the pids. The operation is in-place. - - Parameters - ---------- - source : bool - rotate on the input tensor - target : bool - rotate on the output tensor - """ - # rotate - inputpids = br.rotate_flavor_to_unified_evolution if source else None - targetpids = br.rotate_flavor_to_unified_evolution if target else None - flavor_reshape(eko, inputpids=inputpids, targetpids=targetpids) - # assign pids - if source: - eko.rotations._inputpids = br.unified_evol_basis_pids - if target: - eko.rotations._targetpids = br.unified_evol_basis_pids + """ + Rotate the operator into unified evolution basis. + + This also assigns also the pids. The operation is in-place. + + Parameters + ---------- + source : bool + rotate on the input tensor + target : bool + rotate on the output tensor + """ + # rotate + inputpids = br.rotate_flavor_to_unified_evolution if source else None + targetpids = br.rotate_flavor_to_unified_evolution if target else None + flavor_reshape(eko, inputpids=inputpids, targetpids=targetpids) + # assign pids + if source: + eko.rotations._inputpids = br.unified_evol_basis_pids + if target: + eko.rotations._targetpids = br.unified_evol_basis_pids diff --git a/src/eko/scale_variations/expanded.py b/src/eko/scale_variations/expanded.py index bb4f2f6f6..62eb9f547 100644 --- a/src/eko/scale_variations/expanded.py +++ b/src/eko/scale_variations/expanded.py @@ -172,6 +172,7 @@ def singlet_variation(gamma, a_s, order, nf, L): ) return sv_ker + @nb.njit(cache=True) def QEDnon_singlet_variation(gamma, a_s, a_em, alphaem_running, order, nf, L): """Non-singlet scale variation dispatcher. @@ -194,9 +195,9 @@ def QEDnon_singlet_variation(gamma, a_s, a_em, alphaem_running, order, nf, L): complex scale variation kernel """ - if not alphaem_running : + if not alphaem_running: return non_singlet_variation(gamma[1:, 0], a_s, order, nf, L) - else : + else: sv_ker = 1.0 if order[0] >= 2: sv_ker += a_s * variation_as1(gamma[1:, 0], L) @@ -204,15 +205,23 @@ def QEDnon_singlet_variation(gamma, a_s, a_em, alphaem_running, order, nf, L): sv_ker += a_em * variation_as1(gamma[0, 1:], L) if order[0] >= 3: beta0 = beta.beta_qcd_as2(nf) - sv_ker += a_s**2 * variation_as2(gamma[1:, 0], L, beta0, gamma[1,0] ** 2) + sv_ker += a_s**2 * variation_as2(gamma[1:, 0], L, beta0, gamma[1, 0] ** 2) if order[0] >= 4: beta1 = beta.beta_qcd((3, 0), nf) - g0g1 = gamma[1,0] * gamma[2,0] + g0g1 = gamma[1, 0] * gamma[2, 0] sv_ker += a_s**3 * variation_as3( - gamma[1:, 0], L, beta0, beta1, gamma[1,0] ** 2, gamma[1,0] ** 3, g0g1, g0g1 + gamma[1:, 0], + L, + beta0, + beta1, + gamma[1, 0] ** 2, + gamma[1, 0] ** 3, + g0g1, + g0g1, ) return sv_ker + @nb.njit(cache=True) def QEDsinglet_variation(gamma, a_s, a_em, alphaem_running, order, nf, L): """Singlet scale variation dispatcher. @@ -237,7 +246,7 @@ def QEDsinglet_variation(gamma, a_s, a_em, alphaem_running, order, nf, L): """ sv_ker = np.eye(4, dtype=np.complex_) gamma = np.ascontiguousarray(gamma) - if not alphaem_running : + if not alphaem_running: if order[0] >= 2: sv_ker += a_s * variation_as1(gamma[1:, 0], L) if order[0] >= 3: @@ -246,14 +255,14 @@ def QEDsinglet_variation(gamma, a_s, a_em, alphaem_running, order, nf, L): sv_ker += a_s**2 * variation_as2(gamma[1:, 0], L, beta0, gamma10e2) if order[0] >= 4: beta1 = beta.beta_qcd((3, 0), nf) - gamma10e3 = gamma10e2 @ gamma[1,0] + gamma10e3 = gamma10e2 @ gamma[1, 0] # here the product is not commutative - g20g10 = gamma[2,0] @ gamma[1,0] - g10g20 = gamma[1,0] @ gamma[2,0] + g20g10 = gamma[2, 0] @ gamma[1, 0] + g10g20 = gamma[1, 0] @ gamma[2, 0] sv_ker += a_s**3 * variation_as3( gamma[1:, 0], L, beta0, beta1, gamma10e2, gamma10e3, g20g10, g10g20 ) - else : + else: if order[0] >= 2: sv_ker += a_s * variation_as1(gamma[1:, 0], L) if order[1] >= 2: @@ -264,15 +273,16 @@ def QEDsinglet_variation(gamma, a_s, a_em, alphaem_running, order, nf, L): sv_ker += a_s**2 * variation_as2(gamma[1:, 0], L, beta0, gamma10e2) if order[0] >= 4: beta1 = beta.beta_qcd((3, 0), nf) - gamma10e3 = gamma10e2 @ gamma[1,0] + gamma10e3 = gamma10e2 @ gamma[1, 0] # here the product is not commutative - g20g10 = gamma[2,0] @ gamma[1,0] - g10g20 = gamma[1,0] @ gamma[2,0] + g20g10 = gamma[2, 0] @ gamma[1, 0] + g10g20 = gamma[1, 0] @ gamma[2, 0] sv_ker += a_s**3 * variation_as3( gamma[1:, 0], L, beta0, beta1, gamma10e2, gamma10e3, g20g10, g10g20 ) return sv_ker + @nb.njit(cache=True) def QEDvalence_variation(gamma, a_s, a_em, alphaem_running, order, nf, L): """Singlet scale variation dispatcher. @@ -297,7 +307,7 @@ def QEDvalence_variation(gamma, a_s, a_em, alphaem_running, order, nf, L): """ sv_ker = np.eye(2, dtype=np.complex_) gamma = np.ascontiguousarray(gamma) - if not alphaem_running : + if not alphaem_running: sv_ker = np.eye(2, dtype=np.complex_) gamma = np.ascontiguousarray(gamma) if order[0] >= 2: @@ -308,14 +318,14 @@ def QEDvalence_variation(gamma, a_s, a_em, alphaem_running, order, nf, L): sv_ker += a_s**2 * variation_as2(gamma[1:, 0], L, beta0, gamma10e2) if order[0] >= 4: beta1 = beta.beta_qcd((3, 0), nf) - gamma10e3 = gamma10e2 @ gamma[1,0] + gamma10e3 = gamma10e2 @ gamma[1, 0] # here the product is not commutative - g20g10 = gamma[2,0] @ gamma[1,0] - g10g20 = gamma[1,0] @ gamma[2,0] + g20g10 = gamma[2, 0] @ gamma[1, 0] + g10g20 = gamma[1, 0] @ gamma[2, 0] sv_ker += a_s**3 * variation_as3( gamma[1:, 0], L, beta0, beta1, gamma10e2, gamma10e3, g20g10, g10g20 ) - else : + else: sv_ker = np.eye(2, dtype=np.complex_) gamma = np.ascontiguousarray(gamma) if order[0] >= 2: @@ -328,10 +338,10 @@ def QEDvalence_variation(gamma, a_s, a_em, alphaem_running, order, nf, L): sv_ker += a_s**2 * variation_as2(gamma[1:, 0], L, beta0, gamma10e2) if order[0] >= 4: beta1 = beta.beta_qcd((3, 0), nf) - gamma10e3 = gamma10e2 @ gamma[1,0] + gamma10e3 = gamma10e2 @ gamma[1, 0] # here the product is not commutative - g20g10 = gamma[2,0] @ gamma[1,0] - g10g20 = gamma[1,0] @ gamma[2,0] + g20g10 = gamma[2, 0] @ gamma[1, 0] + g10g20 = gamma[1, 0] @ gamma[2, 0] sv_ker += a_s**3 * variation_as3( gamma[1:, 0], L, beta0, beta1, gamma10e2, gamma10e3, g20g10, g10g20 ) diff --git a/src/eko/scale_variations/exponentiated.py b/src/eko/scale_variations/exponentiated.py index 0449da6a3..48b1ae333 100644 --- a/src/eko/scale_variations/exponentiated.py +++ b/src/eko/scale_variations/exponentiated.py @@ -67,9 +67,7 @@ def gamma_variation_qed(gamma, order, nf, L, alphaem_running): gamma : numpy.ndarray adjusted anomalous dimensions """ - # since we are modifying *in-place* be carefull, that the order matters! - # and indeed, we need to adjust the high elements first - if not alphaem_running : + if not alphaem_running: # if alphaem is fixed then only alphas is varied so gamma[0,1] and gamma[0,2] # don't get a variation while gamma[1,1] gets a variation that is O(as2aem1) # that we are neglecting @@ -103,6 +101,6 @@ def gamma_variation_qed(gamma, order, nf, L, alphaem_running): # we are neglecting the order (2,1) # if order[1] >= 1: # gamma[2, 1] -= -L * (beta01qcd * gamma[1,0] + beta0qcd * gamma[1,1]) - if order[1] >= 2 : + if order[1] >= 2: gamma[0, 2] -= beta0qed * gamma[0, 1] * L return gamma diff --git a/tests/eko/test_beta.py b/tests/eko/test_beta.py index 33533e2cc..1813296c4 100644 --- a/tests/eko/test_beta.py +++ b/tests/eko/test_beta.py @@ -29,7 +29,7 @@ def test_beta_aem2(): """Test first beta function coefficient""" # from hep-ph/9803211 np.testing.assert_approx_equal( - beta.beta_qed_aem2(5), -4.0 / 3 * (3 + 3*(2 * 4 / 9 + 3 * 1 / 9)) + beta.beta_qed_aem2(5), -4.0 / 3 * (3 + 3 * (2 * 4 / 9 + 3 * 1 / 9)) ) @@ -44,7 +44,7 @@ def test_beta_aem3(): """Test second beta function coefficient""" # from hep-ph/9803211 np.testing.assert_approx_equal( - beta.beta_qed_aem3(5), -4.0 * (3 + 3*(2 * 16 / 81 + 3 * 1 / 81)) + beta.beta_qed_aem3(5), -4.0 * (3 + 3 * (2 * 16 / 81 + 3 * 1 / 81)) ) diff --git a/tests/eko/test_couplings.py b/tests/eko/test_couplings.py index 4de25e879..7cb8d068b 100644 --- a/tests/eko/test_couplings.py +++ b/tests/eko/test_couplings.py @@ -677,7 +677,7 @@ def test_compute_aem_as(self): for a in a_values: aem = couplings.compute_aem_as(a_ref[1], a_ref[0], a[0], nf=4) np.testing.assert_allclose(aem, a[1], atol=1e-6, rtol=1e-4) - + def test_as_aem(self): # prepare thresh_setups = [ @@ -690,10 +690,19 @@ def test_as_aem(self): scale_ref = 91.0**2 for thresh_setup in thresh_setups: for qcd in range(1, 4 + 1): - for qed in range(2+1): + for qed in range(2 + 1): for mode_ev in ["expanded", "exact"]: - for q2 in [1.**2, 3.**2, 4.**2, 10.**2, 50**2, 100**2, 150.**2, 180.**2]: - for fact_scale_ratio in [0.5**2, 1.**2, 2.**2]: + for q2 in [ + 1.0**2, + 3.0**2, + 4.0**2, + 10.0**2, + 50**2, + 100**2, + 150.0**2, + 180.0**2, + ]: + for fact_scale_ratio in [0.5**2, 1.0**2, 2.0**2]: for alphaem_running in [True, False]: coupling = Couplings( np.array([alphas_ref, alphaem_ref]), @@ -705,8 +714,20 @@ def test_as_aem(self): alphaem_running=alphaem_running, ) np.testing.assert_allclose( - coupling.a(q2, fact_scale=q2*fact_scale_ratio)[0], coupling.a_s(q2,fact_scale=q2*fact_scale_ratio), rtol=1e-10 + coupling.a( + q2, fact_scale=q2 * fact_scale_ratio + )[0], + coupling.a_s( + q2, fact_scale=q2 * fact_scale_ratio + ), + rtol=1e-10, ) np.testing.assert_allclose( - coupling.a(q2,fact_scale=q2*fact_scale_ratio)[1], coupling.a_em(q2,fact_scale=q2*fact_scale_ratio), rtol=1e-10 - ) \ No newline at end of file + coupling.a( + q2, fact_scale=q2 * fact_scale_ratio + )[1], + coupling.a_em( + q2, fact_scale=q2 * fact_scale_ratio + ), + rtol=1e-10, + ) diff --git a/tests/eko/test_ev_operator.py b/tests/eko/test_ev_operator.py index 447fc61a7..6df2fe6d5 100644 --- a/tests/eko/test_ev_operator.py +++ b/tests/eko/test_ev_operator.py @@ -454,7 +454,7 @@ def test_aem_list(self): ocard["configs"]["ev_op_iterations"] = 10 for qcd in range(1, 3 + 1): for qed in range(1, 2 + 1): - for q0 in [np.sqrt(2.), 2., 4.5]: + for q0 in [np.sqrt(2.0), 2.0, 4.5]: for q2to in ocard["Q2grid"]: for aem_running in [True, False]: tcard["order"] = (qcd, qed) @@ -468,9 +468,13 @@ def test_aem_list(self): InterpolatorDispatcher( XGrid( operators_card["xgrid"], - log=operators_card["configs"]["interpolation_is_log"], + log=operators_card["configs"][ + "interpolation_is_log" + ], ), - operators_card["configs"]["interpolation_polynomial_degree"], + operators_card["configs"][ + "interpolation_polynomial_degree" + ], ), ) o = Operator(g.config, g.managers, 3, q0**2, q2to) @@ -488,7 +492,9 @@ def test_aem_list(self): as_half, 3, ) - np.testing.assert_allclose(aem, aem_list[step], rtol=1e-4) + np.testing.assert_allclose( + aem, aem_list[step], rtol=1e-4 + ) as_l = as_h def check_lo(self, o): diff --git a/tests/eko/test_kernels_QEDns.py b/tests/eko/test_kernels_QEDns.py index 9178526ba..e18734fd4 100644 --- a/tests/eko/test_kernels_QEDns.py +++ b/tests/eko/test_kernels_QEDns.py @@ -34,7 +34,15 @@ def test_zero(): for method in methods: np.testing.assert_allclose( ns.dispatcher( - order, method, gamma_ns, 1.0, 1.0, [1.0, 1.0], False, nf, ev_op_iterations + order, + method, + gamma_ns, + 1.0, + 1.0, + [1.0, 1.0], + False, + nf, + ev_op_iterations, ), 1.0, ) @@ -69,7 +77,15 @@ def test_zero_true_gamma(): for method in methods: np.testing.assert_allclose( ns.dispatcher( - order, method, gamma_ns, 1.0, 1.0, [1.0, 1.0], False, nf, ev_op_iterations + order, + method, + gamma_ns, + 1.0, + 1.0, + [1.0, 1.0], + False, + nf, + ev_op_iterations, ), 1.0, ) @@ -116,13 +132,21 @@ def test_ode(): betatot = 0.0 for i in range(0, order[0] + 1): for j in range(0, order[1] + 1): - gammatot += gamma_ns[i, j] * a1**i * aem_list[0]**j - betatot += a1**1 * betaQCD[i, j] * a1**i * aem_list[0]**j + gammatot += gamma_ns[i, j] * a1**i * aem_list[0] ** j + betatot += a1**1 * betaQCD[i, j] * a1**i * aem_list[0] ** j r = gammatot / betatot for method in methods: rhs = r * ns.dispatcher( - order, method, gamma_ns, a1, a0, aem_list, False, nf, ev_op_iterations + order, + method, + gamma_ns, + a1, + a0, + aem_list, + False, + nf, + ev_op_iterations, ) lhs = ( ns.dispatcher( @@ -174,13 +198,23 @@ def test_ode_true_gamma(): betatot = 0.0 for i in range(0, order[0] + 1): for j in range(0, order[1] + 1): - gammatot += gamma_ns[i, j] * a1**i * aem_list[0]**j - betatot += a1**1 * betaQCD[i, j] * a1**i * aem_list[0]**j + gammatot += gamma_ns[i, j] * a1**i * aem_list[0] ** j + betatot += ( + a1**1 * betaQCD[i, j] * a1**i * aem_list[0] ** j + ) r = gammatot / betatot for method in methods: rhs = r * ns.dispatcher( - order, method, gamma_ns, a1, a0, aem_list, False, nf, ev_op_iterations + order, + method, + gamma_ns, + a1, + a0, + aem_list, + False, + nf, + ev_op_iterations, ) lhs = ( ns.dispatcher( @@ -212,5 +246,13 @@ def test_ode_true_gamma(): def test_error(): with pytest.raises(NotImplementedError): ns.dispatcher( - (4, 2), "iterate-exact", np.random.rand(4, 3), 0.2, 0.1, [0.01], False, 3, 10 + (4, 2), + "iterate-exact", + np.random.rand(4, 3), + 0.2, + 0.1, + [0.01], + False, + 3, + 10, ) diff --git a/tests/eko/test_kernels_QEDsinglet.py b/tests/eko/test_kernels_QEDsinglet.py index 7f9e466ff..fec703e01 100644 --- a/tests/eko/test_kernels_QEDsinglet.py +++ b/tests/eko/test_kernels_QEDsinglet.py @@ -52,7 +52,7 @@ def test_zero(monkeypatch): gamma_s, 1, 1, - [1,1], + [1, 1], nf, ev_op_iterations, ev_op_max_order, @@ -66,7 +66,7 @@ def test_zero(monkeypatch): np.zeros((qcd + 1, qed + 1, 4, 4), dtype=complex), 2, 1, - [1,1], + [1, 1], nf, ev_op_iterations, ev_op_max_order, @@ -106,7 +106,7 @@ def test_zero_true_gamma(monkeypatch): gamma_s, 1, 1, - [1,1], + [1, 1], nf, ev_op_iterations, ev_op_max_order, @@ -120,7 +120,7 @@ def test_zero_true_gamma(monkeypatch): np.zeros((qcd + 1, qed + 1, 4, 4), dtype=complex), 2, 1, - [1,1], + [1, 1], nf, ev_op_iterations, ev_op_max_order, diff --git a/tests/eko/test_kernels_QEDvalence.py b/tests/eko/test_kernels_QEDvalence.py index e894d91f6..8df72e285 100644 --- a/tests/eko/test_kernels_QEDvalence.py +++ b/tests/eko/test_kernels_QEDvalence.py @@ -50,7 +50,7 @@ def test_zero(monkeypatch): gamma_v, 1, 1, - [1,1], + [1, 1], nf, ev_op_iterations, ev_op_max_order, @@ -64,7 +64,7 @@ def test_zero(monkeypatch): np.zeros((qcd + 1, qed + 1, 2, 2), dtype=complex), 2, 1, - [1,1], + [1, 1], nf, ev_op_iterations, ev_op_max_order, @@ -102,7 +102,7 @@ def test_zero_true_gamma(monkeypatch): gamma_v, 1, 1, - [1,1], + [1, 1], nf, ev_op_iterations, ev_op_max_order, @@ -116,7 +116,7 @@ def test_zero_true_gamma(monkeypatch): np.zeros((qcd + 1, qed + 1, 2, 2), dtype=complex), 2, 1, - [1,1], + [1, 1], nf, ev_op_iterations, ev_op_max_order, diff --git a/tests/eko/test_sv_exponentiated.py b/tests/eko/test_sv_exponentiated.py index dd42ff51c..941be8160 100644 --- a/tests/eko/test_sv_exponentiated.py +++ b/tests/eko/test_sv_exponentiated.py @@ -2,7 +2,7 @@ import numpy as np -from eko.scale_variations.exponentiated import gamma_variation +from eko.scale_variations.exponentiated import gamma_variation, gamma_variation_qed def test_gamma_ns_fact(): @@ -31,3 +31,23 @@ def test_gamma_singlet_fact(): assert gamma_s_NNLO_1[2] - gamma_s[2] == 8.0 gamma_s_N3LO_0 = gamma_variation(gamma_s.copy(), (4, 0), 3, 0) assert gamma_s_N3LO_0[3] == gamma_s[3] + + +def test_gamma_ns_qed_fact(): + gamma_ns = np.array( + [ + [0.0, 1.0, 0.5, 0.25, 0.125], + [0.2, 0.1, 0.0, 0.0, 0.0], + [0.15, 0.0, 0.0, 0.0, 0.0], + ] + ).transpose() + gamma_ns_as1aem1_0 = gamma_variation_qed(gamma_ns.copy(), (1, 1), 3, 0, True) + np.testing.assert_allclose(gamma_ns_as1aem1_0, gamma_ns) + gamma_ns_as1aem1_1 = gamma_variation_qed(gamma_ns.copy(), (1, 1), 3, 1, True) + np.testing.assert_allclose(gamma_ns_as1aem1_1, gamma_ns) + gamma_ns_as2aem2_1 = gamma_variation_qed(gamma_ns.copy(), (2, 2), 3, 1, True) + assert gamma_ns_as2aem2_1[2, 0] < gamma_ns[2, 0] + assert gamma_ns_as2aem2_1[0, 2] > gamma_ns[0, 2] # beta0qed < 0 + gamma_ns_as4aem2_1 = gamma_variation_qed(gamma_ns.copy(), (4, 2), 3, 1, True) + gamma_ns_N3LO_1 = gamma_variation(gamma_ns.copy()[1:, 0], (4, 0), 3, 1) + np.testing.assert_allclose(gamma_ns_as4aem2_1[1:, 0], gamma_ns_N3LO_1) From 834431f9a26a6202d476ac3c2abfae465b31e2bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 21 Oct 2022 15:21:15 +0200 Subject: [PATCH 210/312] Test dispatcher for expanded sv qed --- tests/eko/test_sv_expanded.py | 41 +++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/tests/eko/test_sv_expanded.py b/tests/eko/test_sv_expanded.py index eae6e9b96..05add78c6 100644 --- a/tests/eko/test_sv_expanded.py +++ b/tests/eko/test_sv_expanded.py @@ -30,6 +30,19 @@ def test_ns_sv_dispacher(): ) +def test_ns_sv_dispacher_qed(): + """Test to identity""" + order = (4, 2) + gamma_ns = np.random.rand(order[0], order[1]) + L = 0 + nf = 5 + a_s = 0.35 + a_em = 0.01 + np.testing.assert_allclose( + expanded.QEDnon_singlet_variation(gamma_ns, a_s, a_em, True, order, nf, L), 1.0 + ) + + def test_singlet_sv_dispacher(): """Test to identity""" order = (4, 0) @@ -42,6 +55,34 @@ def test_singlet_sv_dispacher(): ) +def test_singlet_sv_dispacher_qed(): + """Test to identity""" + order = (4, 2) + gamma_singlet = np.random.rand(order[0], order[1], 4, 4) + L = 0 + nf = 5 + a_s = 0.35 + a_em = 0.01 + np.testing.assert_allclose( + expanded.QEDsinglet_variation(gamma_singlet, a_s, a_em, True, order, nf, L), + np.eye(4), + ) + + +def test_valence_sv_dispacher_qed(): + """Test to identity""" + order = (4, 2) + gamma_valence = np.random.rand(order[0], order[1], 2, 2) + L = 0 + nf = 5 + a_s = 0.35 + a_em = 0.01 + np.testing.assert_allclose( + expanded.QEDvalence_variation(gamma_valence, a_s, a_em, True, order, nf, L), + np.eye(2), + ) + + def test_scale_variation_a_vs_b(): r""" Test ``ModSV=exponentiated`` kernel vs ``ModSV=expanded``. From d4d3dd1cab0bec7853d498ae0f740b7f4a793cf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 21 Oct 2022 15:25:16 +0200 Subject: [PATCH 211/312] Test also fixed alphaem --- tests/eko/test_sv_expanded.py | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/tests/eko/test_sv_expanded.py b/tests/eko/test_sv_expanded.py index 05add78c6..72e2de3de 100644 --- a/tests/eko/test_sv_expanded.py +++ b/tests/eko/test_sv_expanded.py @@ -38,9 +38,13 @@ def test_ns_sv_dispacher_qed(): nf = 5 a_s = 0.35 a_em = 0.01 - np.testing.assert_allclose( - expanded.QEDnon_singlet_variation(gamma_ns, a_s, a_em, True, order, nf, L), 1.0 - ) + for alphaem_running in [True, False]: + np.testing.assert_allclose( + expanded.QEDnon_singlet_variation( + gamma_ns, a_s, a_em, alphaem_running, order, nf, L + ), + 1.0, + ) def test_singlet_sv_dispacher(): @@ -63,10 +67,13 @@ def test_singlet_sv_dispacher_qed(): nf = 5 a_s = 0.35 a_em = 0.01 - np.testing.assert_allclose( - expanded.QEDsinglet_variation(gamma_singlet, a_s, a_em, True, order, nf, L), - np.eye(4), - ) + for alphaem_running in [True, False]: + np.testing.assert_allclose( + expanded.QEDsinglet_variation( + gamma_singlet, a_s, a_em, alphaem_running, order, nf, L + ), + np.eye(4), + ) def test_valence_sv_dispacher_qed(): @@ -77,10 +84,13 @@ def test_valence_sv_dispacher_qed(): nf = 5 a_s = 0.35 a_em = 0.01 - np.testing.assert_allclose( - expanded.QEDvalence_variation(gamma_valence, a_s, a_em, True, order, nf, L), - np.eye(2), - ) + for alphaem_running in [True, False]: + np.testing.assert_allclose( + expanded.QEDvalence_variation( + gamma_valence, a_s, a_em, alphaem_running, order, nf, L + ), + np.eye(2), + ) def test_scale_variation_a_vs_b(): From 1c3a2c96be1290aaa3468d8a10b78a4dfd8873f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 21 Oct 2022 15:45:29 +0200 Subject: [PATCH 212/312] Test all kernel/QEDns --- tests/eko/test_kernels_QEDns.py | 119 +++++++++++++++++--------------- 1 file changed, 62 insertions(+), 57 deletions(-) diff --git a/tests/eko/test_kernels_QEDns.py b/tests/eko/test_kernels_QEDns.py index e18734fd4..24b48c3e2 100644 --- a/tests/eko/test_kernels_QEDns.py +++ b/tests/eko/test_kernels_QEDns.py @@ -25,6 +25,7 @@ def test_zero(): """No evolution results in exp(0)""" nf = 3 ev_op_iterations = 2 + running_alpha = [True, False] for qcd in range(1, 3 + 1): for qed in range(0, 2 + 1): order = (qcd, qed) @@ -32,49 +33,7 @@ def test_zero(): np.random.rand(qcd + 1, qed + 1) + np.random.rand(qcd + 1, qed + 1) * 1j ) for method in methods: - np.testing.assert_allclose( - ns.dispatcher( - order, - method, - gamma_ns, - 1.0, - 1.0, - [1.0, 1.0], - False, - nf, - ev_op_iterations, - ), - 1.0, - ) - np.testing.assert_allclose( - ns.dispatcher( - order, - method, - np.zeros((qcd + 1, qed + 1), dtype=complex), - 2.0, - 1.0, - [1.0, 1.0], - False, - nf, - ev_op_iterations, - ), - 1.0, - ) - - -def test_zero_true_gamma(): - """No evolution results in exp(0)""" - nf = 3 - ev_op_iterations = 2 - for mode in br.non_singlet_pids_map.values(): - if mode in [10201, 10101, 10200]: - continue - for qcd in range(1, 3 + 1): - for qed in range(0, 2 + 1): - order = (qcd, qed) - n = np.random.rand() - gamma_ns = ad.gamma_ns_qed(order, mode, n, nf) - for method in methods: + for running in running_alpha: np.testing.assert_allclose( ns.dispatcher( order, @@ -83,7 +42,7 @@ def test_zero_true_gamma(): 1.0, 1.0, [1.0, 1.0], - False, + running, nf, ev_op_iterations, ), @@ -97,7 +56,7 @@ def test_zero_true_gamma(): 2.0, 1.0, [1.0, 1.0], - False, + running, nf, ev_op_iterations, ), @@ -105,6 +64,51 @@ def test_zero_true_gamma(): ) +def test_zero_true_gamma(): + """No evolution results in exp(0)""" + nf = 3 + ev_op_iterations = 2 + running_alpha = [True, False] + for mode in br.non_singlet_pids_map.values(): + if mode in [10201, 10101, 10200]: + continue + for qcd in range(1, 3 + 1): + for qed in range(0, 2 + 1): + order = (qcd, qed) + n = np.random.rand() + gamma_ns = ad.gamma_ns_qed(order, mode, n, nf) + for method in methods: + for running in running_alpha: + np.testing.assert_allclose( + ns.dispatcher( + order, + method, + gamma_ns, + 1.0, + 1.0, + [1.0, 1.0], + running, + nf, + ev_op_iterations, + ), + 1.0, + ) + np.testing.assert_allclose( + ns.dispatcher( + order, + method, + np.zeros((qcd + 1, qed + 1), dtype=complex), + 2.0, + 1.0, + [1.0, 1.0], + running, + nf, + ev_op_iterations, + ), + 1.0, + ) + + def test_ode(): nf = 3 ev_op_iterations = 10 @@ -244,15 +248,16 @@ def test_ode_true_gamma(): def test_error(): - with pytest.raises(NotImplementedError): - ns.dispatcher( - (4, 2), - "iterate-exact", - np.random.rand(4, 3), - 0.2, - 0.1, - [0.01], - False, - 3, - 10, - ) + for running in [True, False]: + with pytest.raises(NotImplementedError): + ns.dispatcher( + (4, 2), + "iterate-exact", + np.random.rand(4, 3), + 0.2, + 0.1, + [0.01], + running, + 3, + 10, + ) From 3123bba511562ad050f53c68c88a6b7670943df3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 24 Oct 2022 10:50:53 +0200 Subject: [PATCH 213/312] Rename exp_singlet -> exp_matrix_2D and call it in QEDvalence --- src/eko/anomalous_dimensions/__init__.py | 14 +++++++------- src/eko/kernels/QEDvalence.py | 2 +- src/eko/kernels/singlet.py | 12 ++++++------ tests/eko/test_ad.py | 6 +++--- tests/eko/test_kernels_QEDsinglet.py | 4 ++-- tests/eko/test_kernels_s.py | 8 ++++---- 6 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index f5e00104a..c66c25506 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -26,13 +26,13 @@ @nb.njit(cache=True) -def exp_singlet(gamma_S): +def exp_matrix_2D(gamma): r""" Compute the exponential and the eigensystem of the singlet anomalous dimension matrix. Parameters ---------- - gamma_S : numpy.ndarray + gamma : numpy.ndarray singlet anomalous dimension matrix Returns @@ -60,15 +60,15 @@ def exp_singlet(gamma_S): """ # compute eigenvalues det = np.sqrt( - np.power(gamma_S[0, 0] - gamma_S[1, 1], 2) + 4.0 * gamma_S[0, 1] * gamma_S[1, 0] + np.power(gamma[0, 0] - gamma[1, 1], 2) + 4.0 * gamma[0, 1] * gamma[1, 0] ) - lambda_p = 1.0 / 2.0 * (gamma_S[0, 0] + gamma_S[1, 1] + det) - lambda_m = 1.0 / 2.0 * (gamma_S[0, 0] + gamma_S[1, 1] - det) + lambda_p = 1.0 / 2.0 * (gamma[0, 0] + gamma[1, 1] + det) + lambda_m = 1.0 / 2.0 * (gamma[0, 0] + gamma[1, 1] - det) # compute projectors identity = np.identity(2, np.complex_) c = 1.0 / det - e_p = +c * (gamma_S - lambda_m * identity) - e_m = -c * (gamma_S - lambda_p * identity) + e_p = +c * (gamma - lambda_m * identity) + e_m = -c * (gamma - lambda_p * identity) exp = e_m * np.exp(lambda_m) + e_p * np.exp(lambda_p) return exp, lambda_p, lambda_m, e_p, e_m diff --git a/src/eko/kernels/QEDvalence.py b/src/eko/kernels/QEDvalence.py index 52a5c7c8d..4b35bba85 100644 --- a/src/eko/kernels/QEDvalence.py +++ b/src/eko/kernels/QEDvalence.py @@ -54,7 +54,7 @@ def eko_iterate(gamma_valence, a1, a0, aem_list, nf, order, ev_op_iterations): ) gamma += gamma_valence[i, j] * a_half**i * aem_list[step] ** j ln = gamma / betatot * delta_a - ek = np.ascontiguousarray(ad.exp_matrix(ln)[0]) + ek = np.ascontiguousarray(ad.exp_matrix_2D(ln)[0]) e = ek @ e al = ah return e diff --git a/src/eko/kernels/singlet.py b/src/eko/kernels/singlet.py index 5b175504e..cbd02b8ca 100644 --- a/src/eko/kernels/singlet.py +++ b/src/eko/kernels/singlet.py @@ -31,7 +31,7 @@ def lo_exact(gamma_singlet, a1, a0, nf): numpy.ndarray singlet leading order exact EKO """ - return ad.exp_singlet(gamma_singlet[0] * ei.j00(a1, a0, nf))[0] + return ad.exp_matrix_2D(gamma_singlet[0] * ei.j00(a1, a0, nf))[0] @nb.njit(cache=True) @@ -58,7 +58,7 @@ def nlo_decompose(gamma_singlet, j01, j11): numpy.ndarray singlet next-to-leading order decompose EKO """ - return ad.exp_singlet(gamma_singlet[0] * j01 + gamma_singlet[1] * j11)[0] + return ad.exp_matrix_2D(gamma_singlet[0] * j01 + gamma_singlet[1] * j11)[0] @nb.njit(cache=True) @@ -137,7 +137,7 @@ def nnlo_decompose(gamma_singlet, j02, j12, j22): numpy.ndarray singlet next-to-next-to-leading order decompose EKO """ - return ad.exp_singlet( + return ad.exp_matrix_2D( gamma_singlet[0] * j02 + gamma_singlet[1] * j12 + gamma_singlet[2] * j22 )[0] @@ -226,7 +226,7 @@ def n3lo_decompose(gamma_singlet, j03, j13, j23, j33): numpy.ndarray singlet |N3LO| decompose EKO """ - return ad.exp_singlet( + return ad.exp_matrix_2D( gamma_singlet[0] * j03 + gamma_singlet[1] * j13 + gamma_singlet[2] * j23 @@ -346,7 +346,7 @@ def eko_iterate(gamma_singlet, a1, a0, nf, order, ev_op_iterations): gamma_summed += gamma_singlet[i] * a_half**i beta_summed += beta_vec[i] * a_half ** (i + 1) ln = gamma_summed / beta_summed * delta_a - ek = np.ascontiguousarray(ad.exp_singlet(ln)[0]) + ek = np.ascontiguousarray(ad.exp_matrix_2D(ln)[0]) e = ek @ e al = ah return e @@ -430,7 +430,7 @@ def u_vec(r, ev_op_max_order): u = np.zeros((ev_op_max_order[0], 2, 2), np.complex_) # k = 0 .. max_order # init u[0] = np.identity(2, np.complex_) - _, r_p, r_m, e_p, e_m = ad.exp_singlet(r[0]) + _, r_p, r_m, e_p, e_m = ad.exp_matrix_2D(r[0]) e_p = np.ascontiguousarray(e_p) e_m = np.ascontiguousarray(e_m) for kk in range(1, ev_op_max_order[0]): diff --git a/tests/eko/test_ad.py b/tests/eko/test_ad.py index 20941dc49..b23fc87ee 100644 --- a/tests/eko/test_ad.py +++ b/tests/eko/test_ad.py @@ -18,7 +18,7 @@ def test_eigensystem_gamma_singlet_0_values(): n = 3 s1 = harmonics.S1(n) gamma_S_0 = as1.gamma_singlet(3, s1, NF) - res = ad.exp_singlet(gamma_S_0) + res = ad.exp_matrix_2D(gamma_S_0) lambda_p = complex(12.273612971466964, 0) lambda_m = complex(5.015275917421917, 0) e_p = np.array( @@ -40,7 +40,7 @@ def test_exp_matrix(): n = 3 s1 = harmonics.S1(n) gamma_S_0 = as1.gamma_singlet(3, s1, NF) - res = ad.exp_singlet(gamma_S_0)[0] + res = ad.exp_matrix_2D(gamma_S_0)[0] res2 = ad.exp_matrix(gamma_S_0)[0] assert_allclose(res, res2) gamma_S_0_qed = as1.gamma_QEDsinglet(3, s1, NF) @@ -70,7 +70,7 @@ def test_eigensystem_gamma_singlet_projectors_EV(): # ignore Runtime Warnings warnings.simplefilter("ignore", RuntimeWarning) for gamma_S in ad.gamma_singlet(o, N, nf): - _exp, l_p, l_m, e_p, e_m = ad.exp_singlet(gamma_S) + _exp, l_p, l_m, e_p, e_m = ad.exp_matrix_2D(gamma_S) # projectors behave as P_a . P_b = delta_ab P_a assert_allclose(np.dot(e_p, e_p), e_p) assert_almost_equal(np.dot(e_p, e_m), np.zeros((2, 2))) diff --git a/tests/eko/test_kernels_QEDsinglet.py b/tests/eko/test_kernels_QEDsinglet.py index fec703e01..47adb089e 100644 --- a/tests/eko/test_kernels_QEDsinglet.py +++ b/tests/eko/test_kernels_QEDsinglet.py @@ -33,7 +33,7 @@ def test_zero(monkeypatch): ) # monkeypatch.setattr( # ad, - # "exp_singlet", + # "exp_matrix_2D", # lambda gamma_S: ( # gamma_S, # 1, @@ -87,7 +87,7 @@ def test_zero_true_gamma(monkeypatch): gamma_s = ad.gamma_singlet_qed(order, n, nf) # monkeypatch.setattr( # ad, - # "exp_singlet", + # "exp_matrix_2D", # lambda gamma_S: ( # gamma_S, # 1, diff --git a/tests/eko/test_kernels_s.py b/tests/eko/test_kernels_s.py index bead14709..18fedddba 100644 --- a/tests/eko/test_kernels_s.py +++ b/tests/eko/test_kernels_s.py @@ -27,7 +27,7 @@ def test_zero_lo(monkeypatch): gamma_s = np.random.rand(1, 2, 2) + np.random.rand(1, 2, 2) * 1j monkeypatch.setattr( ad, - "exp_singlet", + "exp_matrix_2D", lambda gamma_S: ( gamma_S, 1, @@ -66,7 +66,7 @@ def test_zero_nlo_decompose(monkeypatch): gamma_s = np.random.rand(2, 2, 2) + np.random.rand(2, 2, 2) * 1j monkeypatch.setattr( ad, - "exp_singlet", + "exp_matrix_2D", lambda gamma_S: ( gamma_S, 1, @@ -108,7 +108,7 @@ def test_zero_nnlo_decompose(monkeypatch): gamma_s = np.random.rand(3, 2, 2) + np.random.rand(3, 2, 2) * 1j monkeypatch.setattr( ad, - "exp_singlet", + "exp_matrix_2D", lambda gamma_S: ( gamma_S, 1, @@ -150,7 +150,7 @@ def test_zero_n3lo_decompose(monkeypatch): gamma_s = np.random.rand(4, 2, 2) + np.random.rand(4, 2, 2) * 1j monkeypatch.setattr( ad, - "exp_singlet", + "exp_matrix_2D", lambda gamma_S: ( gamma_S, 1, From 88702305c85098d6d6f77849735ee52823325940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 24 Oct 2022 11:09:47 +0200 Subject: [PATCH 214/312] Fix test_kernels_QEDvalence.py --- tests/eko/test_kernels_QEDvalence.py | 52 ++++++++++++++-------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/tests/eko/test_kernels_QEDvalence.py b/tests/eko/test_kernels_QEDvalence.py index 8df72e285..ee6136e04 100644 --- a/tests/eko/test_kernels_QEDvalence.py +++ b/tests/eko/test_kernels_QEDvalence.py @@ -31,17 +31,17 @@ def test_zero(monkeypatch): np.random.rand(qcd + 1, qed + 1, 2, 2) + np.random.rand(qcd + 1, qed + 1, 2, 2) * 1j ) - # monkeypatch.setattr( - # ad, - # "exp_matrix", - # lambda gamma_S: ( - # gamma_S, - # 1, - # 1, - # np.array([[1, 0], [0, 0]]), - # np.array([[0, 0], [0, 1]]), - # ), - # ) + monkeypatch.setattr( + ad, + "exp_matrix_2D", + lambda gamma_v: ( + gamma_v, + 1, + 1, + np.array([[1, 0], [0, 0]]), + np.array([[0, 0], [0, 1]]), + ), + ) for method in methods: np.testing.assert_allclose( val.dispatcher( @@ -55,7 +55,7 @@ def test_zero(monkeypatch): ev_op_iterations, ev_op_max_order, ), - np.eye(2), + np.zeros((2, 2)), ) np.testing.assert_allclose( val.dispatcher( @@ -69,7 +69,7 @@ def test_zero(monkeypatch): ev_op_iterations, ev_op_max_order, ), - np.eye(2), + np.zeros((2, 2)), ) @@ -83,17 +83,17 @@ def test_zero_true_gamma(monkeypatch): order = (qcd, qed) n = np.random.rand() gamma_v = ad.gamma_valence_qed(order, n, nf) - # monkeypatch.setattr( - # ad, - # "exp_matrix", - # lambda gamma_S: ( - # gamma_S, - # 1, - # 1, - # np.array([[1, 0], [0, 0]]), - # np.array([[0, 0], [0, 1]]), - # ), - # ) + monkeypatch.setattr( + ad, + "exp_matrix_2D", + lambda gamma_v: ( + gamma_v, + 1, + 1, + np.array([[1, 0], [0, 0]]), + np.array([[0, 0], [0, 1]]), + ), + ) for method in methods: np.testing.assert_allclose( val.dispatcher( @@ -107,7 +107,7 @@ def test_zero_true_gamma(monkeypatch): ev_op_iterations, ev_op_max_order, ), - np.eye(2), + np.zeros((2, 2)), ) np.testing.assert_allclose( val.dispatcher( @@ -121,7 +121,7 @@ def test_zero_true_gamma(monkeypatch): ev_op_iterations, ev_op_max_order, ), - np.eye(2), + np.zeros((2, 2)), ) From c662303402ba0a49dd833dc76b5e20d9ef031da6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 26 Oct 2022 10:29:58 +0200 Subject: [PATCH 215/312] Compute ad only once when construct matrices --- src/eko/anomalous_dimensions/aem1.py | 19 ++++--- src/eko/anomalous_dimensions/aem2.py | 69 +++++++++++-------------- src/eko/anomalous_dimensions/as1aem1.py | 29 ++++++----- 3 files changed, 57 insertions(+), 60 deletions(-) diff --git a/src/eko/anomalous_dimensions/aem1.py b/src/eko/anomalous_dimensions/aem1.py index b410489b1..23fa3d456 100644 --- a/src/eko/anomalous_dimensions/aem1.py +++ b/src/eko/anomalous_dimensions/aem1.py @@ -133,26 +133,29 @@ def gamma_singlet(N, nf, sx): vue2m = constants.vue2m(nf) vde2m = constants.vde2m(nf) e2delta = vde2m - vue2m + e2avg + gamma_ph_q = gamma_phq(N) + gamma_q_ph = gamma_qph(N, nf) + gamma_nonsinglet = gamma_ns(N, sx) gamma_S_01 = np.array( [ [0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j], [ 0.0 + 0.0j, gamma_phph(nf), - e2avg * gamma_phq(N), - vue2m * gamma_phq(N), + e2avg * gamma_ph_q, + vue2m * gamma_ph_q, ], [ 0.0 + 0.0j, - e2avg * gamma_qph(N, nf), - e2avg * gamma_ns(N, sx), - vue2m * gamma_ns(N, sx), + e2avg * gamma_q_ph, + e2avg * gamma_nonsinglet, + vue2m * gamma_nonsinglet, ], [ 0.0 + 0.0j, - vde2m * gamma_qph(N, nf), - vde2m * gamma_ns(N, sx), - e2delta * gamma_ns(N, sx), + vde2m * gamma_q_ph, + vde2m * gamma_nonsinglet, + e2delta * gamma_nonsinglet, ], ], np.complex_, diff --git a/src/eko/anomalous_dimensions/aem2.py b/src/eko/anomalous_dimensions/aem2.py index bfcd3f990..10a66b748 100644 --- a/src/eko/anomalous_dimensions/aem2.py +++ b/src/eko/anomalous_dimensions/aem2.py @@ -352,50 +352,47 @@ def gamma_singlet(N, nf, sx): vd = nd / nf e2avg = constants.e2avg(nf) e2m = constants.eu2 - constants.ed2 + gamma_ph_u = gamma_phu(N, nf, sx) + gamma_ph_d = gamma_phd(N, nf, sx) + gamma_u_ph = gamma_uph(N, nf, sx) + gamma_d_ph = gamma_dph(N, nf, sx) + gamma_ns_p_u = gamma_nspu(N, nf, sx) + gamma_ns_p_d = gamma_nspd(N, nf, sx) + gamma_pure_singlet = gamma_ps(N, nf) gamma_S_02 = np.array( [ [0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j], [ 0.0 + 0.0j, gamma_phph(N, nf), - vu * constants.eu2 * gamma_phu(N, nf, sx) - + vd * constants.ed2 * gamma_phd(N, nf, sx), - vu - * ( - constants.eu2 * gamma_phu(N, nf, sx) - - constants.ed2 * gamma_phd(N, nf, sx) - ), + vu * constants.eu2 * gamma_ph_u + vd * constants.ed2 * gamma_ph_d, + vu * (constants.eu2 * gamma_ph_u - constants.ed2 * gamma_ph_d), ], [ 0.0 + 0.0j, - vu * constants.eu2 * gamma_uph(N, nf, sx) - + vd * constants.ed2 * gamma_dph(N, nf, sx), - vu * constants.eu2 * gamma_nspu(N, nf, sx) - + vd * constants.ed2 * gamma_nspd(N, nf, sx) - + e2avg**2 * gamma_ps(N, nf), + vu * constants.eu2 * gamma_u_ph + vd * constants.ed2 * gamma_d_ph, + vu * constants.eu2 * gamma_ns_p_u + + vd * constants.ed2 * gamma_ns_p_d + + e2avg**2 * gamma_pure_singlet, vu * ( - constants.eu2 * gamma_nspu(N, nf, sx) - - constants.ed2 * gamma_nspd(N, nf, sx) - + e2m * e2avg * gamma_ps(N, nf) + constants.eu2 * gamma_ns_p_u + - constants.ed2 * gamma_ns_p_d + + e2m * e2avg * gamma_pure_singlet ), ], [ 0.0 + 0.0j, + vd * (constants.eu2 * gamma_u_ph - constants.ed2 * gamma_d_ph), vd * ( - constants.eu2 * gamma_uph(N, nf, sx) - - constants.ed2 * gamma_dph(N, nf, sx) - ), - vd - * ( - constants.eu2 * gamma_nspu(N, nf, sx) - - constants.ed2 * gamma_nspd(N, nf, sx) - + e2m * e2avg * gamma_ps(N, nf) + constants.eu2 * gamma_ns_p_u + - constants.ed2 * gamma_ns_p_d + + e2m * e2avg * gamma_pure_singlet ), - vd * constants.eu2 * gamma_nspu(N, nf, sx) - + vu * constants.ed2 * gamma_nspd(N, nf, sx) - + vu * vd * e2m**2 * gamma_ps(N, nf), + vd * constants.eu2 * gamma_ns_p_u + + vu * constants.ed2 * gamma_ns_p_d + + vu * vd * e2m**2 * gamma_pure_singlet, ], ], np.complex_, @@ -425,25 +422,17 @@ def gamma_valence(N, nf, sx): nd = nf - nu vu = nu / nf vd = nd / nf + gamma_ns_m_u = gamma_nsmu(N, nf, sx) + gamma_ns_m_d = gamma_nsmd(N, nf, sx) gamma_V_02 = np.array( [ [ - vu * constants.eu2 * gamma_nsmu(N, nf, sx) - + vd * constants.ed2 * gamma_nsmd(N, nf, sx), - vu - * ( - constants.eu2 * gamma_nsmu(N, nf, sx) - - constants.ed2 * gamma_nsmd(N, nf, sx) - ), + vu * constants.eu2 * gamma_ns_m_u + vd * constants.ed2 * gamma_ns_m_d, + vu * (constants.eu2 * gamma_ns_m_u - constants.ed2 * gamma_ns_m_d), ], [ - vd - * ( - constants.eu2 * gamma_nsmu(N, nf, sx) - - constants.ed2 * gamma_nsmd(N, nf, sx) - ), - vd * constants.eu2 * gamma_nsmu(N, nf, sx) - + vu * constants.ed2 * gamma_nsmd(N, nf, sx), + vd * (constants.eu2 * gamma_ns_m_u - constants.ed2 * gamma_ns_m_d), + vd * constants.eu2 * gamma_ns_m_u + vu * constants.ed2 * gamma_ns_m_d, ], ], np.complex_, diff --git a/src/eko/anomalous_dimensions/as1aem1.py b/src/eko/anomalous_dimensions/as1aem1.py index ad3a97193..850b77b4a 100644 --- a/src/eko/anomalous_dimensions/as1aem1.py +++ b/src/eko/anomalous_dimensions/as1aem1.py @@ -397,31 +397,36 @@ def gamma_singlet(N, nf, sx): e2_tot = nf * e2avg vue2m = constants.vue2m(nf) vde2m = constants.vde2m(nf) + gamma_g_q = gamma_gq(N, sx) + gamma_ph_q = gamma_phq(N, sx) + gamma_q_g = gamma_qg(N, nf, sx) + gamma_q_ph = gamma_qph(N, nf, sx) + gamma_ns_p = gamma_nsp(N, sx) gamma_S_11 = np.array( [ [ e2_tot * gamma_gg(), e2_tot * gamma_gph(N), - e2avg * gamma_gq(N, sx), - vue2m * gamma_gq(N, sx), + e2avg * gamma_g_q, + vue2m * gamma_g_q, ], [ e2_tot * gamma_phg(N), gamma_phph(nf), - e2avg * gamma_phq(N, sx), - vue2m * gamma_phq(N, sx), + e2avg * gamma_ph_q, + vue2m * gamma_ph_q, ], [ - e2avg * gamma_qg(N, nf, sx), - e2avg * gamma_qph(N, nf, sx), - e2avg * gamma_nsp(N, sx), - vue2m * gamma_nsp(N, sx), + e2avg * gamma_q_g, + e2avg * gamma_q_ph, + e2avg * gamma_ns_p, + vue2m * gamma_ns_p, ], [ - vde2m * gamma_qg(N, nf, sx), - vde2m * gamma_qph(N, nf, sx), - vde2m * gamma_nsp(N, sx), - e2delta * gamma_nsp(N, sx), + vde2m * gamma_q_g, + vde2m * gamma_q_ph, + vde2m * gamma_ns_p, + e2delta * gamma_ns_p, ], ], np.complex_, From adcbc8bd2f11980a281091d8ff5aad70a077d5b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 26 Oct 2022 10:35:18 +0200 Subject: [PATCH 216/312] Compute pure QCD gammas only once when construct matrices --- src/eko/anomalous_dimensions/as2.py | 5 +++-- src/eko/anomalous_dimensions/as3.py | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/eko/anomalous_dimensions/as2.py b/src/eko/anomalous_dimensions/as2.py index 7642f4673..339d36484 100644 --- a/src/eko/anomalous_dimensions/as2.py +++ b/src/eko/anomalous_dimensions/as2.py @@ -308,13 +308,14 @@ def gamma_QEDsinglet(N, nf, sx): gamma_gq : :math:`\gamma_{gq}^{(0)}` gamma_gg : :math:`\gamma_{gg}^{(0)}` """ - gamma_qq = gamma_nsp(N, nf, sx) + gamma_ps(N, nf) + gamma_ns_p = gamma_nsp(N, nf, sx) + gamma_qq = gamma_ns_p + gamma_ps(N, nf) gamma_S = np.array( [ [gamma_gg(N, nf, sx), 0.0 + 0.0j, gamma_gq(N, nf, sx), 0.0 + 0.0j], [0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j], [gamma_qg(N, nf, sx), 0.0 + 0.0j, gamma_qq, 0.0 + 0.0j], - [0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, gamma_nsp(N, nf, sx)], + [0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, gamma_ns_p], ], np.complex_, ) diff --git a/src/eko/anomalous_dimensions/as3.py b/src/eko/anomalous_dimensions/as3.py index e18d73569..9099ba3f7 100644 --- a/src/eko/anomalous_dimensions/as3.py +++ b/src/eko/anomalous_dimensions/as3.py @@ -620,13 +620,14 @@ def gamma_QEDsinglet(N, nf, sx): gamma_gq : :math:`\gamma_{gq}^{(0)}` gamma_gg : :math:`\gamma_{gg}^{(0)}` """ - gamma_qq = gamma_nsp(N, nf, sx) + gamma_ps(N, nf, sx) + gamma_np_p = gamma_nsp(N, nf, sx) + gamma_qq = gamma_np_p + gamma_ps(N, nf, sx) gamma_S = np.array( [ [gamma_gg(N, nf, sx), 0.0 + 0.0j, gamma_gq(N, nf, sx), 0.0 + 0.0j], [0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j], [gamma_qg(N, nf, sx), 0.0 + 0.0j, gamma_qq, 0.0 + 0.0j], - [0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, gamma_nsp(N, nf, sx)], + [0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, gamma_np_p], ], np.complex_, ) From cb65ed3d0d2601c5027961e3563ad3c40d80948b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 26 Oct 2022 11:31:48 +0200 Subject: [PATCH 217/312] Remove np.complex from beta function in kernels --- src/eko/anomalous_dimensions/as1aem1.py | 4 ++-- src/eko/kernels/QEDsinglet.py | 6 ++---- src/eko/kernels/QEDvalence.py | 6 ++---- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/eko/anomalous_dimensions/as1aem1.py b/src/eko/anomalous_dimensions/as1aem1.py index 850b77b4a..c7e09720d 100644 --- a/src/eko/anomalous_dimensions/as1aem1.py +++ b/src/eko/anomalous_dimensions/as1aem1.py @@ -393,10 +393,10 @@ def gamma_singlet(N, nf, sx): O(as1aem1) singlet anomalous dimension :math:`\\gamma_{S}^{(1,1)}(N,nf,sx)` """ e2avg = constants.e2avg(nf) - e2delta = constants.vde2m(nf) - constants.vue2m(nf) + constants.e2avg(nf) - e2_tot = nf * e2avg vue2m = constants.vue2m(nf) vde2m = constants.vde2m(nf) + e2delta = vde2m - vue2m + e2avg + e2_tot = nf * e2avg gamma_g_q = gamma_gq(N, sx) gamma_ph_q = gamma_phq(N, sx) gamma_q_g = gamma_qg(N, nf, sx) diff --git a/src/eko/kernels/QEDsinglet.py b/src/eko/kernels/QEDsinglet.py index abec1db37..38db7a290 100644 --- a/src/eko/kernels/QEDsinglet.py +++ b/src/eko/kernels/QEDsinglet.py @@ -37,7 +37,7 @@ def eko_iterate(gamma_singlet, a1, a0, aem_list, nf, order, ev_op_iterations): a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) e = np.identity(4, np.complex_) al = a_steps[0] - betaQCD = np.zeros((4, 3), np.complex_) + betaQCD = np.zeros((4, 3)) for i in range(1, 3 + 1): betaQCD[i, 0] = beta.beta_qcd((i + 1, 0), nf) betaQCD[1, 1] = beta.beta_qcd((2, 1), nf) @@ -48,9 +48,7 @@ def eko_iterate(gamma_singlet, a1, a0, aem_list, nf, order, ev_op_iterations): betatot = 0 for i in range(0, order[0] + 1): for j in range(0, order[1] + 1): - betatot += ( - a_half**1 * betaQCD[i, j] * a_half**i * aem_list[step] ** j - ) + betatot += betaQCD[i, j] * a_half ** (i + 1) * aem_list[step] ** j gamma += gamma_singlet[i, j] * a_half**i * aem_list[step] ** j ln = gamma / betatot * delta_a ek = np.ascontiguousarray(ad.exp_matrix(ln)[0]) diff --git a/src/eko/kernels/QEDvalence.py b/src/eko/kernels/QEDvalence.py index 4b35bba85..d03e04bd4 100644 --- a/src/eko/kernels/QEDvalence.py +++ b/src/eko/kernels/QEDvalence.py @@ -38,7 +38,7 @@ def eko_iterate(gamma_valence, a1, a0, aem_list, nf, order, ev_op_iterations): a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) e = np.identity(2, np.complex_) al = a_steps[0] - betaQCD = np.zeros((4, 3), np.complex_) + betaQCD = np.zeros((4, 3)) for i in range(1, 3 + 1): betaQCD[i, 0] = beta.beta_qcd((i + 1, 0), nf) betaQCD[1, 1] = beta.beta_qcd((2, 1), nf) @@ -49,9 +49,7 @@ def eko_iterate(gamma_valence, a1, a0, aem_list, nf, order, ev_op_iterations): betatot = 0 for i in range(0, order[0] + 1): for j in range(0, order[1] + 1): - betatot += ( - a_half**1 * betaQCD[i, j] * a_half**i * aem_list[step] ** j - ) + betatot += betaQCD[i, j] * a_half ** (i + 1) * aem_list[step] ** j gamma += gamma_valence[i, j] * a_half**i * aem_list[step] ** j ln = gamma / betatot * delta_a ek = np.ascontiguousarray(ad.exp_matrix_2D(ln)[0]) From d4818ef601ad196a1d89d98a8cb1c2f4d2e02136 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 26 Oct 2022 12:13:04 +0200 Subject: [PATCH 218/312] Move alphaem_running and aem_list in init --- src/eko/evolution_operator/__init__.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 418e7afae..660201764 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -421,6 +421,8 @@ def __init__( self.is_threshold = is_threshold self.op_members = {} self.order = tuple(config["order"]) + self.alphaem_running = self.managers["couplings"].alphaem_running + self.aem_list_as = self.compute_aem_list_as() @property def n_pools(self): @@ -485,13 +487,7 @@ def a_em(self): """Return the computed values for :math:`a_{em}`.""" return (self.a[0][1], self.a[1][1]) - @property - def alphaem_running(self): - """Return the value of alphaem_running""" - return self.managers["couplings"].alphaem_running - - @property - def aem_list_as(self): + def compute_aem_list_as(self): """Return the list of the couplings for the different values of :math:`a_s`.""" if self.order[1] == 0: return np.array([]) From 7a313ab4c8f4e65bceda4ba57a8c8eadd5f6fb85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 26 Oct 2022 12:40:15 +0200 Subject: [PATCH 219/312] Move computation of a in init --- src/eko/evolution_operator/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 660201764..ecd8e40cd 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -422,6 +422,7 @@ def __init__( self.op_members = {} self.order = tuple(config["order"]) self.alphaem_running = self.managers["couplings"].alphaem_running + self.a = self.compute_a() self.aem_list_as = self.compute_aem_list_as() @property @@ -465,8 +466,7 @@ def mur2_shift(self, q2): return q2 / self.fact_to_ren return q2 - @property - def a(self): + def compute_a(self): """Return the computed values for :math:`a_s` and :math:`a_{em}`.""" coupling = self.managers["couplings"] a0 = coupling.a( From 9dc116c1952420201731b648fbcc154a416dd687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 26 Oct 2022 14:27:28 +0200 Subject: [PATCH 220/312] Fix test_ev_operator.py --- tests/eko/test_ev_operator.py | 62 ++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 5 deletions(-) diff --git a/tests/eko/test_ev_operator.py b/tests/eko/test_ev_operator.py index 6df2fe6d5..a405af0e3 100644 --- a/tests/eko/test_ev_operator.py +++ b/tests/eko/test_ev_operator.py @@ -322,14 +322,30 @@ def test_quad_ker(monkeypatch): class TestOperator: def test_labels(self): + tcard = copy.deepcopy(theory_card) + ocard = copy.deepcopy(operators_card) + g = OperatorGrid.from_dict( + tcard, + ocard, + ThresholdsAtlas.from_dict(tcard), + Couplings.from_dict(tcard), + InterpolatorDispatcher( + XGrid( + operators_card["xgrid"], + log=operators_card["configs"]["interpolation_is_log"], + ), + operators_card["configs"]["interpolation_polynomial_degree"], + ), + ) o = Operator( dict( order=(3, 0), debug_skip_non_singlet=False, debug_skip_singlet=False, n_integration_cores=1, + ModSV=None, ), - {}, + g.managers, 3, 1, 2, @@ -341,8 +357,9 @@ def test_labels(self): debug_skip_non_singlet=True, debug_skip_singlet=True, n_integration_cores=1, + ModSV=None, ), - {}, + g.managers, 3, 1, 2, @@ -350,14 +367,31 @@ def test_labels(self): assert sorted(o.labels) == [] def test_labels_qed(self): + tcard = copy.deepcopy(theory_card) + ocard = copy.deepcopy(operators_card) + g = OperatorGrid.from_dict( + tcard, + ocard, + ThresholdsAtlas.from_dict(tcard), + Couplings.from_dict(tcard), + InterpolatorDispatcher( + XGrid( + operators_card["xgrid"], + log=operators_card["configs"]["interpolation_is_log"], + ), + operators_card["configs"]["interpolation_polynomial_degree"], + ), + ) o = Operator( dict( order=(3, 1), debug_skip_non_singlet=False, debug_skip_singlet=False, n_integration_cores=1, + ModSV=None, + ev_op_iterations=1, ), - {}, + g.managers, 3, 1, 2, @@ -369,8 +403,10 @@ def test_labels_qed(self): debug_skip_non_singlet=True, debug_skip_singlet=True, n_integration_cores=1, + ModSV=None, + ev_op_iterations=1, ), - {}, + g.managers, 3, 1, 2, @@ -382,14 +418,30 @@ def test_n_pools(self): # make sure we actually have more the those cores (e.g. on github we don't) if os.cpu_count() <= excluded_cores: return + tcard = copy.deepcopy(theory_card) + ocard = copy.deepcopy(operators_card) + g = OperatorGrid.from_dict( + tcard, + ocard, + ThresholdsAtlas.from_dict(tcard), + Couplings.from_dict(tcard), + InterpolatorDispatcher( + XGrid( + operators_card["xgrid"], + log=operators_card["configs"]["interpolation_is_log"], + ), + operators_card["configs"]["interpolation_polynomial_degree"], + ), + ) o = Operator( dict( order=(2, 0), debug_skip_non_singlet=True, debug_skip_singlet=True, n_integration_cores=-excluded_cores, + ModSV=None, ), - {}, + g.managers, 3, 1, 10, From 4432bfdaf1e84f86af553a05fd68a460d7aca564 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 26 Oct 2022 14:29:16 +0200 Subject: [PATCH 221/312] Revert "Move computation of a in init" This reverts commit 7a313ab4c8f4e65bceda4ba57a8c8eadd5f6fb85. --- src/eko/evolution_operator/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index ecd8e40cd..660201764 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -422,7 +422,6 @@ def __init__( self.op_members = {} self.order = tuple(config["order"]) self.alphaem_running = self.managers["couplings"].alphaem_running - self.a = self.compute_a() self.aem_list_as = self.compute_aem_list_as() @property @@ -466,7 +465,8 @@ def mur2_shift(self, q2): return q2 / self.fact_to_ren return q2 - def compute_a(self): + @property + def a(self): """Return the computed values for :math:`a_s` and :math:`a_{em}`.""" coupling = self.managers["couplings"] a0 = coupling.a( From a8b97c3db9b12aa197b638ab80142f430a73387f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 26 Oct 2022 18:15:02 +0200 Subject: [PATCH 222/312] Move computation of a in init --- src/eko/anomalous_dimensions/as1aem1.py | 1 - src/eko/evolution_operator/__init__.py | 7 ++++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/eko/anomalous_dimensions/as1aem1.py b/src/eko/anomalous_dimensions/as1aem1.py index c7e09720d..3c026e709 100644 --- a/src/eko/anomalous_dimensions/as1aem1.py +++ b/src/eko/anomalous_dimensions/as1aem1.py @@ -341,7 +341,6 @@ def gamma_nsm(N, sx): g3N = harmonics.g_functions.mellin_g3(N, S1) S1p2 = harmonics.polygamma.recursive_harmonic_sum(S1, N, 2, 1) g3Np2 = harmonics.g_functions.mellin_g3(N + 2.0, S1p2) - result = ( -32.0 * zeta2 * S1h - 8.0 / (N + N**2) * S2h diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 660201764..4b64cb676 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -422,7 +422,9 @@ def __init__( self.op_members = {} self.order = tuple(config["order"]) self.alphaem_running = self.managers["couplings"].alphaem_running - self.aem_list_as = self.compute_aem_list_as() + if self.log_label == "Evolution": + self.a = self.compute_a() + self.aem_list_as = self.compute_aem_list_as() @property def n_pools(self): @@ -465,8 +467,7 @@ def mur2_shift(self, q2): return q2 / self.fact_to_ren return q2 - @property - def a(self): + def compute_a(self): """Return the computed values for :math:`a_s` and :math:`a_{em}`.""" coupling = self.managers["couplings"] a0 = coupling.a( From 5a1bec3e2e889eae18b35266c0fb820b7d6dff0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 28 Oct 2022 17:40:26 +0200 Subject: [PATCH 223/312] Speed up QED non singlet running alphaem --- src/eko/anomalous_dimensions/aem2.py | 10 +++--- src/eko/kernels/QEDnon_singlet.py | 46 ++++++++++++++++++++-------- 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/src/eko/anomalous_dimensions/aem2.py b/src/eko/anomalous_dimensions/aem2.py index 10a66b748..4fff36181 100644 --- a/src/eko/anomalous_dimensions/aem2.py +++ b/src/eko/anomalous_dimensions/aem2.py @@ -254,12 +254,12 @@ def gamma_nsmu(N, nf, sx): eSigma2 = constants.NC * (nu * constants.eu2 + nd * constants.ed2) tmp = ( 2 - * (-12 + 20 * N + 47 * N**2 + 6 * N**3 + 3 * N**4) - / (9.0 * N**2 * (1 + N) ** 2) - - 80 / 9 * S1 - + 16 / 3 * S2 + * (-12.0 + 20.0 * N + 47.0 * N**2 + 6.0 * N**3 + 3.0 * N**4) + / (9.0 * N**2 * (1.0 + N) ** 2) + - 80.0 / 9.0 * S1 + + 16.0 / 3.0 * S2 ) * eSigma2 - return constants.eu2 * as1aem1.gamma_nsm(N, sx) / constants.CF / 2 + tmp + return constants.eu2 * as1aem1.gamma_nsm(N, sx) / constants.CF / 2.0 + tmp @nb.njit(cache=True) diff --git a/src/eko/kernels/QEDnon_singlet.py b/src/eko/kernels/QEDnon_singlet.py index 9a864e534..fa6f934ec 100644 --- a/src/eko/kernels/QEDnon_singlet.py +++ b/src/eko/kernels/QEDnon_singlet.py @@ -316,28 +316,48 @@ def running_alphaem_exact(order, gamma_ns, a1, a0, aem_list, nf, ev_op_iteration # TODO : implement it with np.prod in a way that numba compiles it if order[1] == 1: if order[0] == 1: - for step, ah in enumerate(a_steps[1:]): - res *= lo_aem1_exact(gamma_ns, ah, a_steps[step], aem_list[step], nf) + for step in range(1, ev_op_iterations + 1): + res *= lo_aem1_exact( + gamma_ns, a_steps[step], a_steps[step - 1], aem_list[step - 1], nf + ) return res if order[0] == 2: - for step, ah in enumerate(a_steps[1:]): - res *= nlo_aem1_exact(gamma_ns, ah, a_steps[step], aem_list[step], nf) + for step in range(1, ev_op_iterations + 1): + res *= nlo_aem1_exact( + gamma_ns, a_steps[step], a_steps[step - 1], aem_list[step - 1], nf + ) return res if order[0] == 3: - for step, ah in enumerate(a_steps[1:]): - res *= nnlo_aem1_exact(gamma_ns, ah, a_steps[step], aem_list[step], nf) - return res + for step in range(1, ev_op_iterations + 1): + res *= nnlo_aem1_exact( + gamma_ns, a_steps[step], a_steps[step - 1], aem_list[step - 1], nf + ) if order[1] == 2: if order[0] == 1: - for step, ah in enumerate(a_steps[1:]): - res *= lo_aem2_exact(gamma_ns, ah, a_steps[step], aem_list[step], nf) + for step in range(1, ev_op_iterations + 1): + res *= lo_aem2_exact( + gamma_ns, a_steps[step], a_steps[step - 1], aem_list[step - 1], nf + ) return res if order[0] == 2: - for step, ah in enumerate(a_steps[1:]): - res *= nlo_aem2_exact(gamma_ns, ah, a_steps[step], aem_list[step], nf) + for step in range(1, ev_op_iterations + 1): + res *= nlo_aem2_exact( + gamma_ns, a_steps[step], a_steps[step - 1], aem_list[step - 1], nf + ) return res if order[0] == 3: - for step, ah in enumerate(a_steps[1:]): - res *= nnlo_aem2_exact(gamma_ns, ah, a_steps[step], aem_list[step], nf) + for step in range(1, ev_op_iterations + 1): + res *= nnlo_aem2_exact( + gamma_ns, a_steps[step], a_steps[step - 1], aem_list[step - 1], nf + ) + # res = np.prod( + # [ + # nnlo_aem2_exact( + # gamma_ns, a_steps[i], + # a_steps[i-1], aem_list[step], + # nf + # ) for i in range(1, 1 + ev_op_iterations) + # ] + # ) return res raise NotImplementedError("Selected order is not implemented") From e798b96f98984b152c36227558c57560d3573b7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 28 Oct 2022 18:08:23 +0200 Subject: [PATCH 224/312] Fix bug in last commit --- src/eko/kernels/QEDnon_singlet.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/eko/kernels/QEDnon_singlet.py b/src/eko/kernels/QEDnon_singlet.py index fa6f934ec..69f78e579 100644 --- a/src/eko/kernels/QEDnon_singlet.py +++ b/src/eko/kernels/QEDnon_singlet.py @@ -332,6 +332,7 @@ def running_alphaem_exact(order, gamma_ns, a1, a0, aem_list, nf, ev_op_iteration res *= nnlo_aem1_exact( gamma_ns, a_steps[step], a_steps[step - 1], aem_list[step - 1], nf ) + return res if order[1] == 2: if order[0] == 1: for step in range(1, ev_op_iterations + 1): From dde7f55ec2222635e8de96f26a1a68e9e5061bdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 28 Oct 2022 19:03:01 +0200 Subject: [PATCH 225/312] Move computation of harmonic sums to cache --- src/eko/anomalous_dimensions/__init__.py | 38 +++++++++++-------- src/eko/anomalous_dimensions/aem2.py | 36 +++++++++++------- src/eko/anomalous_dimensions/as1aem1.py | 46 +++++++++++------------ src/eko/harmonics/__init__.py | 48 ++++++++++++++++++++++++ tests/eko/test_ad_aem2.py | 14 +++++-- tests/eko/test_ad_as1aem1.py | 6 ++- 6 files changed, 128 insertions(+), 60 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index c66c25506..05b11bb60 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -288,6 +288,8 @@ def gamma_ns_qed(order, mode, n, nf): sx = harmonics.sx(n, max_weight=max_weight + 1) else: sx = harmonics.sx(n, max_weight=3) + if order[1] >= 1: + sx_ns_qed = harmonics.compute_qed_ns_cache(n, sx[0]) # now combine gamma_ns = np.zeros((order[0] + 1, order[1] + 1), np.complex_) if order[0] >= 1: @@ -295,7 +297,7 @@ def gamma_ns_qed(order, mode, n, nf): if order[1] >= 1: gamma_ns[0, 1] = choose_ns_ad_aem1(mode, n, sx) if order[0] >= 1 and order[1] >= 1: - gamma_ns[1, 1] = choose_ns_ad_as1aem1(mode, n, sx) + gamma_ns[1, 1] = choose_ns_ad_as1aem1(mode, n, sx, sx_ns_qed) # NLO and beyond if order[0] >= 2: if mode in [10102, 10103]: @@ -306,7 +308,7 @@ def gamma_ns_qed(order, mode, n, nf): else: raise NotImplementedError("Non-singlet sector is not implemented") if order[1] >= 2: - gamma_ns[0, 2] = choose_ns_ad_aem2(mode, n, nf, sx) + gamma_ns[0, 2] = choose_ns_ad_aem2(mode, n, nf, sx, sx_ns_qed) # NNLO and beyond if order[0] >= 3: if mode in [10102, 10103]: @@ -344,7 +346,7 @@ def choose_ns_ad_aem1(mode, n, sx): @nb.njit(cache=True) -def choose_ns_ad_as1aem1(mode, n, sx): +def choose_ns_ad_as1aem1(mode, n, sx, sx_ns_qed): r""" Select the non-singlet anomalous dimension at O(as1aem1) with the correct charge factor. @@ -363,17 +365,17 @@ def choose_ns_ad_as1aem1(mode, n, sx): non-singlet anomalous dimensions """ if mode == 10102: - return constants.eu2 * as1aem1.gamma_nsp(n, sx) + return constants.eu2 * as1aem1.gamma_nsp(n, sx, sx_ns_qed) elif mode == 10103: - return constants.ed2 * as1aem1.gamma_nsp(n, sx) + return constants.ed2 * as1aem1.gamma_nsp(n, sx, sx_ns_qed) elif mode == 10202: - return constants.eu2 * as1aem1.gamma_nsm(n, sx) + return constants.eu2 * as1aem1.gamma_nsm(n, sx, sx_ns_qed) elif mode == 10203: - return constants.ed2 * as1aem1.gamma_nsm(n, sx) + return constants.ed2 * as1aem1.gamma_nsm(n, sx, sx_ns_qed) @nb.njit(cache=True) -def choose_ns_ad_aem2(mode, n, nf, sx): +def choose_ns_ad_aem2(mode, n, nf, sx, sx_ns_qed): r""" Select the non-singlet anomalous dimension at O(aem2) with the correct charge factor. @@ -392,13 +394,13 @@ def choose_ns_ad_aem2(mode, n, nf, sx): non-singlet anomalous dimensions """ if mode == 10102: - return constants.eu2 * aem2.gamma_nspu(n, nf, sx) + return constants.eu2 * aem2.gamma_nspu(n, nf, sx, sx_ns_qed) elif mode == 10103: - return constants.ed2 * aem2.gamma_nspd(n, nf, sx) + return constants.ed2 * aem2.gamma_nspd(n, nf, sx, sx_ns_qed) elif mode == 10202: - return constants.eu2 * aem2.gamma_nsmu(n, nf, sx) + return constants.eu2 * aem2.gamma_nsmu(n, nf, sx, sx_ns_qed) elif mode == 10203: - return constants.ed2 * aem2.gamma_nsmd(n, nf, sx) + return constants.ed2 * aem2.gamma_nsmd(n, nf, sx, sx_ns_qed) @nb.njit(cache=True) @@ -436,17 +438,19 @@ def gamma_singlet_qed(order, n, nf): sx = harmonics.sx(n, max_weight=max_weight + 1) else: sx = harmonics.sx(n, max_weight=3) + if order[1] >= 1: + sx_ns_qed = harmonics.compute_qed_ns_cache(n, sx[0]) gamma_s = np.zeros((order[0] + 1, order[1] + 1, 4, 4), np.complex_) if order[0] >= 1: gamma_s[1, 0] = as1.gamma_QEDsinglet(n, sx[0], nf) if order[1] >= 1: gamma_s[0, 1] = aem1.gamma_singlet(n, nf, sx) if order[0] >= 1 and order[1] >= 1: - gamma_s[1, 1] = as1aem1.gamma_singlet(n, nf, sx) + gamma_s[1, 1] = as1aem1.gamma_singlet(n, nf, sx, sx_ns_qed) if order[0] >= 2: gamma_s[2, 0] = as2.gamma_QEDsinglet(n, nf, sx) if order[1] >= 2: - gamma_s[0, 2] = aem2.gamma_singlet(n, nf, sx) + gamma_s[0, 2] = aem2.gamma_singlet(n, nf, sx, sx_ns_qed) if order[0] >= 3: gamma_s[3, 0] = as3.gamma_QEDsinglet(n, nf, sx) return gamma_s @@ -488,17 +492,19 @@ def gamma_valence_qed(order, n, nf): sx = harmonics.sx(n, max_weight=max_weight + 1) else: sx = harmonics.sx(n, max_weight=3) + if order[1] >= 1: + sx_ns_qed = harmonics.compute_qed_ns_cache(n, sx[0]) gamma_v = np.zeros((order[0] + 1, order[1] + 1, 2, 2), np.complex_) if order[0] >= 1: gamma_v[1, 0] = as1.gamma_QEDvalence(n, sx[0]) if order[1] >= 1: gamma_v[0, 1] = aem1.gamma_valence(n, nf, sx) if order[0] >= 1 and order[1] >= 1: - gamma_v[1, 1] = as1aem1.gamma_valence(n, nf, sx) + gamma_v[1, 1] = as1aem1.gamma_valence(n, nf, sx, sx_ns_qed) if order[0] >= 2: gamma_v[2, 0] = as2.gamma_QEDvalence(n, nf, sx) if order[1] >= 2: - gamma_v[0, 2] = aem2.gamma_valence(n, nf, sx) + gamma_v[0, 2] = aem2.gamma_valence(n, nf, sx, sx_ns_qed) if order[0] >= 3: gamma_v[3, 0] = as3.gamma_QEDvalence(n, nf, sx) return gamma_v diff --git a/src/eko/anomalous_dimensions/aem2.py b/src/eko/anomalous_dimensions/aem2.py index 4fff36181..6851ba498 100644 --- a/src/eko/anomalous_dimensions/aem2.py +++ b/src/eko/anomalous_dimensions/aem2.py @@ -152,7 +152,7 @@ def gamma_phd(N, nf, sx): @nb.njit(cache=True) -def gamma_nspu(N, nf, sx): +def gamma_nspu(N, nf, sx, sx_ns_qed): r"""Compute the O(aem2) singlet-like non-singlet anomalous dimension for up quarks. Implements sum of Eqs. (57-58) of :cite:`deFlorian:2016gvk` for q=u. @@ -185,11 +185,13 @@ def gamma_nspu(N, nf, sx): - 80.0 / 9.0 * S1 + 16.0 / 3.0 * S2 ) * eSigma2 - return constants.eu2 * as1aem1.gamma_nsp(N, sx) / constants.CF / 2.0 + tmp + return ( + constants.eu2 * as1aem1.gamma_nsp(N, sx, sx_ns_qed) / constants.CF / 2.0 + tmp + ) @nb.njit(cache=True) -def gamma_nspd(N, nf, sx): +def gamma_nspd(N, nf, sx, sx_ns_qed): r"""Compute the O(aem2) singlet-like non-singlet anomalous dimension for down quarks. Implements sum of Eqs. (57-58) of :cite:`deFlorian:2016gvk` for q=d. @@ -222,11 +224,13 @@ def gamma_nspd(N, nf, sx): - 80.0 / 9.0 * S1 + 16.0 / 3.0 * S2 ) * eSigma2 - return constants.ed2 * as1aem1.gamma_nsp(N, sx) / constants.CF / 2.0 + tmp + return ( + constants.ed2 * as1aem1.gamma_nsp(N, sx, sx_ns_qed) / constants.CF / 2.0 + tmp + ) @nb.njit(cache=True) -def gamma_nsmu(N, nf, sx): +def gamma_nsmu(N, nf, sx, sx_ns_qed): r"""Compute the O(aem2) valence-like non-singlet anomalous dimension for up quarks. Implements difference between Eqs. (57-58) of :cite:`deFlorian:2016gvk` for q=u. @@ -259,11 +263,13 @@ def gamma_nsmu(N, nf, sx): - 80.0 / 9.0 * S1 + 16.0 / 3.0 * S2 ) * eSigma2 - return constants.eu2 * as1aem1.gamma_nsm(N, sx) / constants.CF / 2.0 + tmp + return ( + constants.eu2 * as1aem1.gamma_nsm(N, sx, sx_ns_qed) / constants.CF / 2.0 + tmp + ) @nb.njit(cache=True) -def gamma_nsmd(N, nf, sx): +def gamma_nsmd(N, nf, sx, sx_ns_qed): r"""Compute the O(aem2) valence-like non-singlet anomalous dimension for down quarks. Implements difference between Eqs. (57-58) of :cite:`deFlorian:2016gvk` for q=d. @@ -296,7 +302,9 @@ def gamma_nsmd(N, nf, sx): - 80.0 / 9.0 * S1 + 16.0 / 3.0 * S2 ) * eSigma2 - return constants.ed2 * as1aem1.gamma_nsm(N, sx) / constants.CF / 2.0 + tmp + return ( + constants.ed2 * as1aem1.gamma_nsm(N, sx, sx_ns_qed) / constants.CF / 2.0 + tmp + ) @nb.njit(cache=True) @@ -329,7 +337,7 @@ def gamma_ps(N, nf): @nb.njit(cache=True) -def gamma_singlet(N, nf, sx): +def gamma_singlet(N, nf, sx, sx_ns_qed): r"""Compute the O(aem2) singlet sector. Parameters @@ -356,8 +364,8 @@ def gamma_singlet(N, nf, sx): gamma_ph_d = gamma_phd(N, nf, sx) gamma_u_ph = gamma_uph(N, nf, sx) gamma_d_ph = gamma_dph(N, nf, sx) - gamma_ns_p_u = gamma_nspu(N, nf, sx) - gamma_ns_p_d = gamma_nspd(N, nf, sx) + gamma_ns_p_u = gamma_nspu(N, nf, sx, sx_ns_qed) + gamma_ns_p_d = gamma_nspd(N, nf, sx, sx_ns_qed) gamma_pure_singlet = gamma_ps(N, nf) gamma_S_02 = np.array( [ @@ -401,7 +409,7 @@ def gamma_singlet(N, nf, sx): @nb.njit(cache=True) -def gamma_valence(N, nf, sx): +def gamma_valence(N, nf, sx, sx_ns_qed): r"""Compute the O(aem2) valence sector. Parameters @@ -422,8 +430,8 @@ def gamma_valence(N, nf, sx): nd = nf - nu vu = nu / nf vd = nd / nf - gamma_ns_m_u = gamma_nsmu(N, nf, sx) - gamma_ns_m_d = gamma_nsmd(N, nf, sx) + gamma_ns_m_u = gamma_nsmu(N, nf, sx, sx_ns_qed) + gamma_ns_m_d = gamma_nsmd(N, nf, sx, sx_ns_qed) gamma_V_02 = np.array( [ [ diff --git a/src/eko/anomalous_dimensions/as1aem1.py b/src/eko/anomalous_dimensions/as1aem1.py index 3c026e709..84ec6499f 100644 --- a/src/eko/anomalous_dimensions/as1aem1.py +++ b/src/eko/anomalous_dimensions/as1aem1.py @@ -242,7 +242,7 @@ def gamma_gg(): @nb.njit(cache=True) -def gamma_nsp(N, sx): +def gamma_nsp(N, sx, sx_ns_qed): r"""Compute the O(as1aem1) singlet-like non-singlet anomalous dimension. Implements sum of Eqs. (33-34) of :cite:`deFlorian:2015ujt`. @@ -264,15 +264,14 @@ def gamma_nsp(N, sx): S1 = sx[0] S2 = sx[1] S3 = sx[2] - S1h = harmonics.S1(N / 2.0) - S2h = harmonics.S2(N / 2.0) - S3h = harmonics.S3(N / 2.0) - S1p1h = harmonics.S1((N + 1.0) / 2.0) - S2p1h = harmonics.S2((N + 1.0) / 2.0) - S3p1h = harmonics.S3((N + 1.0) / 2.0) - g3N = harmonics.g_functions.mellin_g3(N, S1) - S1p2 = harmonics.polygamma.recursive_harmonic_sum(S1, N, 2, 1) - g3Np2 = harmonics.g_functions.mellin_g3(N + 2.0, S1p2) + S1h = sx_ns_qed[0] + S2h = sx_ns_qed[1] + S3h = sx_ns_qed[2] + S1p1h = sx_ns_qed[3] + S2p1h = sx_ns_qed[4] + S3p1h = sx_ns_qed[5] + g3N = sx_ns_qed[6] + g3Np2 = sx_ns_qed[7] result = ( +32.0 * zeta2 * S1h - 32.0 * zeta2 * S1p1h @@ -310,7 +309,7 @@ def gamma_nsp(N, sx): @nb.njit(cache=True) -def gamma_nsm(N, sx): +def gamma_nsm(N, sx, sx_ns_qed): r"""Compute the O(as1aem1) valence-like non-singlet anomalous dimension. Implements difference between Eqs. (33-34) of :cite:`deFlorian:2015ujt`. @@ -332,15 +331,14 @@ def gamma_nsm(N, sx): S1 = sx[0] S2 = sx[1] S3 = sx[2] - S1h = harmonics.S1(N / 2.0) - S2h = harmonics.S2(N / 2.0) - S3h = harmonics.S3(N / 2.0) - S1p1h = harmonics.S1((N + 1.0) / 2.0) - S2p1h = harmonics.S2((N + 1.0) / 2.0) - S3p1h = harmonics.S3((N + 1.0) / 2.0) - g3N = harmonics.g_functions.mellin_g3(N, S1) - S1p2 = harmonics.polygamma.recursive_harmonic_sum(S1, N, 2, 1) - g3Np2 = harmonics.g_functions.mellin_g3(N + 2.0, S1p2) + S1h = sx_ns_qed[0] + S2h = sx_ns_qed[1] + S3h = sx_ns_qed[2] + S1p1h = sx_ns_qed[3] + S2p1h = sx_ns_qed[4] + S3p1h = sx_ns_qed[5] + g3N = sx_ns_qed[6] + g3Np2 = sx_ns_qed[7] result = ( -32.0 * zeta2 * S1h - 8.0 / (N + N**2) * S2h @@ -374,7 +372,7 @@ def gamma_nsm(N, sx): @nb.njit(cache=True) -def gamma_singlet(N, nf, sx): +def gamma_singlet(N, nf, sx, sx_ns_qed): r"""Compute the O(as1aem1) singlet sector. Parameters @@ -400,7 +398,7 @@ def gamma_singlet(N, nf, sx): gamma_ph_q = gamma_phq(N, sx) gamma_q_g = gamma_qg(N, nf, sx) gamma_q_ph = gamma_qph(N, nf, sx) - gamma_ns_p = gamma_nsp(N, sx) + gamma_ns_p = gamma_nsp(N, sx, sx_ns_qed) gamma_S_11 = np.array( [ [ @@ -434,7 +432,7 @@ def gamma_singlet(N, nf, sx): @nb.njit(cache=True) -def gamma_valence(N, nf, sx): +def gamma_valence(N, nf, sx, sx_ns_qed): r"""Compute the O(as1aem1) valence sector. Parameters @@ -462,4 +460,4 @@ def gamma_valence(N, nf, sx): ], np.complex_, ) - return gamma_V_11 * gamma_nsm(N, sx) + return gamma_V_11 * gamma_nsm(N, sx, sx_ns_qed) diff --git a/src/eko/harmonics/__init__.py b/src/eko/harmonics/__init__.py index 6ba17caf9..b3364a9b7 100644 --- a/src/eko/harmonics/__init__.py +++ b/src/eko/harmonics/__init__.py @@ -6,6 +6,7 @@ import numba as nb import numpy as np +from . import g_functions, polygamma from .w1 import S1, Sm1 from .w2 import S2, Sm2 from .w3 import S3, S21, S2m1, Sm2m1, Sm3, Sm21 @@ -218,3 +219,50 @@ def compute_cache(n, max_weight, is_singlet): sx[3, 1:-1] = s4x(n, sx[:, 0], sx[:, -1], is_singlet) # return list of list keeping the non zero values return [[el for el in sx_list if el != 0] for sx_list in sx] + + +@nb.njit(cache=True) +def compute_qed_ns_cache(n, s1): + r"""Get the harmonics sums cache needed for the qed non singlet AD. + + Parameters + ---------- + n : complex + Mellin moment + s1 : float + harmonic sum :math:`S_1(N)` + + Returns + ------- + list + harmonic sums cache. It contains: + + .. math :: + [S_1(n/2), + S_2(n/2), + S_{3}(n/2), + S_1((n+1)/2), + S_2((n+1)/2), + S_{3}((n+1)/2), + g_3(n), + g_3(n+2)] + + """ + s1h = S1(n / 2.0) + sx_qed_ns = [s1h] + S2h = S2(n / 2.0) + sx_qed_ns.append(S2h) + S3h = S3(n / 2.0) + sx_qed_ns.append(S3h) + S1p1h = S1((n + 1.0) / 2.0) + sx_qed_ns.append(S1p1h) + S2p1h = S2((n + 1.0) / 2.0) + sx_qed_ns.append(S2p1h) + S3p1h = S3((n + 1.0) / 2.0) + sx_qed_ns.append(S3p1h) + g3N = g_functions.mellin_g3(n, s1) + sx_qed_ns.append(g3N) + S1p2 = polygamma.recursive_harmonic_sum(s1, n, 2, 1) + g3Np2 = g_functions.mellin_g3(n + 2.0, S1p2) + sx_qed_ns.append(g3Np2) + return sx_qed_ns diff --git a/tests/eko/test_ad_aem2.py b/tests/eko/test_ad_aem2.py index 93a074af6..d35ef0703 100644 --- a/tests/eko/test_ad_aem2.py +++ b/tests/eko/test_ad_aem2.py @@ -11,9 +11,14 @@ def test_number_conservation(): # number N = complex(1.0, 0.0) sx = h.sx(N, 3) + sx_ns_qed = h.compute_qed_ns_cache(N, sx[0]) for NF in range(2, 6 + 1): - np.testing.assert_almost_equal(ad.aem2.gamma_nsmu(N, NF, sx), 0, decimal=4) - np.testing.assert_almost_equal(ad.aem2.gamma_nsmd(N, NF, sx), 0, decimal=4) + np.testing.assert_almost_equal( + ad.aem2.gamma_nsmu(N, NF, sx, sx_ns_qed), 0, decimal=4 + ) + np.testing.assert_almost_equal( + ad.aem2.gamma_nsmd(N, NF, sx, sx_ns_qed), 0, decimal=4 + ) def test_photon_momentum_conservation(): @@ -35,11 +40,12 @@ def test_quark_momentum_conservation(): # quark momentum N = complex(2.0, 0.0) sx = h.sx(N, 3) + sx_ns_qed = h.compute_qed_ns_cache(N, sx[0]) NF = 6 NU = constants.uplike_flavors(NF) ND = NF - NU np.testing.assert_almost_equal( - ad.aem2.gamma_nspu(N, NF, sx) + ad.aem2.gamma_nspu(N, NF, sx, sx_ns_qed) + constants.eu2 * ad.aem2.gamma_ps(N, NU) + constants.ed2 * ad.aem2.gamma_ps(N, ND) + ad.aem2.gamma_phu(N, NF, sx), @@ -47,7 +53,7 @@ def test_quark_momentum_conservation(): decimal=4, ) np.testing.assert_almost_equal( - ad.aem2.gamma_nspd(N, NF, sx) + ad.aem2.gamma_nspd(N, NF, sx, sx_ns_qed) + constants.eu2 * ad.aem2.gamma_ps(N, NU) + constants.ed2 * ad.aem2.gamma_ps(N, ND) + ad.aem2.gamma_phd(N, NF, sx), diff --git a/tests/eko/test_ad_as1aem1.py b/tests/eko/test_ad_as1aem1.py index 8c1235723..4cc199d09 100644 --- a/tests/eko/test_ad_as1aem1.py +++ b/tests/eko/test_ad_as1aem1.py @@ -12,7 +12,8 @@ def test_number_conservation(): # number N = complex(1.0, 0.0) sx = h.sx(N, 3) - np.testing.assert_almost_equal(ad.as1aem1.gamma_nsm(N, sx), 0, decimal=4) + sx_ns_qed = h.compute_qed_ns_cache(N, sx[0]) + np.testing.assert_almost_equal(ad.as1aem1.gamma_nsm(N, sx, sx_ns_qed), 0, decimal=4) def test_gluon_momentum_conservation(): @@ -53,8 +54,9 @@ def test_quark_momentum_conservation(): # quark momentum N = complex(2.0, 0.0) sx = h.sx(N, 3) + sx_ns_qed = h.compute_qed_ns_cache(N, sx[0]) np.testing.assert_almost_equal( - ad.as1aem1.gamma_nsp(N, sx) + ad.as1aem1.gamma_nsp(N, sx, sx_ns_qed) + ad.as1aem1.gamma_gq(N, sx) + ad.as1aem1.gamma_phq(N, sx), 0, From 795e0483b24167d01f3a6ce3c216f3c17aa6d140 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Sat, 29 Oct 2022 11:39:07 +0200 Subject: [PATCH 226/312] Remove enumerate from QEDsinglet and QEDvalence --- src/eko/kernels/QEDsinglet.py | 10 +++++----- src/eko/kernels/QEDvalence.py | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/eko/kernels/QEDsinglet.py b/src/eko/kernels/QEDsinglet.py index 38db7a290..13dd62b0f 100644 --- a/src/eko/kernels/QEDsinglet.py +++ b/src/eko/kernels/QEDsinglet.py @@ -36,24 +36,24 @@ def eko_iterate(gamma_singlet, a1, a0, aem_list, nf, order, ev_op_iterations): """ a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) e = np.identity(4, np.complex_) - al = a_steps[0] betaQCD = np.zeros((4, 3)) for i in range(1, 3 + 1): betaQCD[i, 0] = beta.beta_qcd((i + 1, 0), nf) betaQCD[1, 1] = beta.beta_qcd((2, 1), nf) - for (step, ah) in enumerate(a_steps[1:]): + for step in range(1, ev_op_iterations + 1): + ah = a_steps[step] + al = a_steps[step - 1] a_half = (ah + al) / 2.0 delta_a = ah - al gamma = np.zeros((4, 4), np.complex_) betatot = 0 for i in range(0, order[0] + 1): for j in range(0, order[1] + 1): - betatot += betaQCD[i, j] * a_half ** (i + 1) * aem_list[step] ** j - gamma += gamma_singlet[i, j] * a_half**i * aem_list[step] ** j + betatot += betaQCD[i, j] * a_half ** (i + 1) * aem_list[step - 1] ** j + gamma += gamma_singlet[i, j] * a_half**i * aem_list[step - 1] ** j ln = gamma / betatot * delta_a ek = np.ascontiguousarray(ad.exp_matrix(ln)[0]) e = ek @ e - al = ah return e diff --git a/src/eko/kernels/QEDvalence.py b/src/eko/kernels/QEDvalence.py index d03e04bd4..fa36d736c 100644 --- a/src/eko/kernels/QEDvalence.py +++ b/src/eko/kernels/QEDvalence.py @@ -37,24 +37,24 @@ def eko_iterate(gamma_valence, a1, a0, aem_list, nf, order, ev_op_iterations): """ a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) e = np.identity(2, np.complex_) - al = a_steps[0] betaQCD = np.zeros((4, 3)) for i in range(1, 3 + 1): betaQCD[i, 0] = beta.beta_qcd((i + 1, 0), nf) betaQCD[1, 1] = beta.beta_qcd((2, 1), nf) - for (step, ah) in enumerate(a_steps[1:]): + for step in range(1, ev_op_iterations + 1): + ah = a_steps[step] + al = a_steps[step - 1] a_half = (ah + al) / 2.0 delta_a = ah - al gamma = np.zeros((2, 2), np.complex_) betatot = 0 for i in range(0, order[0] + 1): for j in range(0, order[1] + 1): - betatot += betaQCD[i, j] * a_half ** (i + 1) * aem_list[step] ** j - gamma += gamma_valence[i, j] * a_half**i * aem_list[step] ** j + betatot += betaQCD[i, j] * a_half ** (i + 1) * aem_list[step - 1] ** j + gamma += gamma_singlet[i, j] * a_half**i * aem_list[step - 1] ** j ln = gamma / betatot * delta_a ek = np.ascontiguousarray(ad.exp_matrix_2D(ln)[0]) e = ek @ e - al = ah return e From 858f606fa063460a8a840e1ac86b23a360304cc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Sat, 29 Oct 2022 11:45:29 +0200 Subject: [PATCH 227/312] Fix typo in last commit --- src/eko/kernels/QEDvalence.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eko/kernels/QEDvalence.py b/src/eko/kernels/QEDvalence.py index fa36d736c..b3863bf43 100644 --- a/src/eko/kernels/QEDvalence.py +++ b/src/eko/kernels/QEDvalence.py @@ -51,7 +51,7 @@ def eko_iterate(gamma_valence, a1, a0, aem_list, nf, order, ev_op_iterations): for i in range(0, order[0] + 1): for j in range(0, order[1] + 1): betatot += betaQCD[i, j] * a_half ** (i + 1) * aem_list[step - 1] ** j - gamma += gamma_singlet[i, j] * a_half**i * aem_list[step - 1] ** j + gamma += gamma_valence[i, j] * a_half**i * aem_list[step - 1] ** j ln = gamma / betatot * delta_a ek = np.ascontiguousarray(ad.exp_matrix_2D(ln)[0]) e = ek @ e From 683126ba7be59241aec2cfe0ddf5b919bb2060bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 31 Oct 2022 15:11:02 +0100 Subject: [PATCH 228/312] Simplify exp_matrix --- src/eko/anomalous_dimensions/__init__.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index 05b11bb60..1ed0a8a1a 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -94,17 +94,13 @@ def exp_matrix(gamma): """ # compute Matrix of coefficients w, v = np.linalg.eig(gamma) - vT = np.transpose(v) - matV = vT @ v - matC = np.linalg.inv(matV) - # compute projectors - tmp = matC @ vT + tmp = np.linalg.inv(v) dim = gamma.shape[0] e = np.zeros((dim, dim, dim), np.complex_) exp = np.zeros((dim, dim), np.complex_) # TODO check if this loop can be entirely cast to numpy for i in range(dim): - e[i] = np.outer(vT[i], tmp[i]) + e[i] = np.outer(v[:, i], tmp[i]) exp += e[i] * np.exp(w[i]) return exp, w, e From b342447027bb58b2cbf8f06d2709f07b79944d60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 4 Nov 2022 11:04:51 +0100 Subject: [PATCH 229/312] Rename QEDsinglet -> singlet_qed and same for non_singlet and valence --- src/eko/anomalous_dimensions/__init__.py | 1 - src/eko/evolution_operator/__init__.py | 6 +++--- src/eko/evolution_operator/flavors.py | 1 - src/eko/kernels/{QEDnon_singlet.py => non_singlet_qed.py} | 0 src/eko/kernels/{QEDsinglet.py => singlet_qed.py} | 0 src/eko/kernels/{QEDvalence.py => valence_qed.py} | 0 tests/eko/test_ev_operator.py | 2 +- tests/eko/test_kernels_QEDns.py | 2 +- tests/eko/test_kernels_QEDsinglet.py | 2 +- tests/eko/test_kernels_QEDvalence.py | 2 +- 10 files changed, 7 insertions(+), 9 deletions(-) rename src/eko/kernels/{QEDnon_singlet.py => non_singlet_qed.py} (100%) rename src/eko/kernels/{QEDsinglet.py => singlet_qed.py} (100%) rename src/eko/kernels/{QEDvalence.py => valence_qed.py} (100%) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index 1ed0a8a1a..bd1178702 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -92,7 +92,6 @@ def exp_matrix(gamma): e : numpy.ndarray projectors on the eigenspaces of the matrix gamma :math:`\gamma(N)` """ - # compute Matrix of coefficients w, v = np.linalg.eig(gamma) tmp = np.linalg.inv(v) dim = gamma.shape[0] diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 4b64cb676..408f9f240 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -18,12 +18,12 @@ from .. import basis_rotation as br from .. import interpolation, mellin from .. import scale_variations as sv -from ..kernels import QEDnon_singlet as qed_ns -from ..kernels import QEDsinglet as qed_s -from ..kernels import QEDvalence as qed_v from ..kernels import non_singlet as ns +from ..kernels import non_singlet_qed as qed_ns from ..kernels import singlet as s +from ..kernels import singlet_qed as qed_s from ..kernels import utils +from ..kernels import valence_qed as qed_v from ..member import OpMember logger = logging.getLogger(__name__) diff --git a/src/eko/evolution_operator/flavors.py b/src/eko/evolution_operator/flavors.py index 6302dbca7..6a7739544 100644 --- a/src/eko/evolution_operator/flavors.py +++ b/src/eko/evolution_operator/flavors.py @@ -195,7 +195,6 @@ def rotate_matching(nf, qed=False, inverse=False): l[f"{qpm}.{oth}"] = b / den else: l[f"{tot}.{tot}"] = 1.0 - # l[f"{tot}.{totdelta}"] = 0.0 l[f"{tot}.{qpm}"] = 1.0 l[f"{totdelta}.{tot}"] = a l[f"{totdelta}.{totdelta}"] = b diff --git a/src/eko/kernels/QEDnon_singlet.py b/src/eko/kernels/non_singlet_qed.py similarity index 100% rename from src/eko/kernels/QEDnon_singlet.py rename to src/eko/kernels/non_singlet_qed.py diff --git a/src/eko/kernels/QEDsinglet.py b/src/eko/kernels/singlet_qed.py similarity index 100% rename from src/eko/kernels/QEDsinglet.py rename to src/eko/kernels/singlet_qed.py diff --git a/src/eko/kernels/QEDvalence.py b/src/eko/kernels/valence_qed.py similarity index 100% rename from src/eko/kernels/QEDvalence.py rename to src/eko/kernels/valence_qed.py diff --git a/tests/eko/test_ev_operator.py b/tests/eko/test_ev_operator.py index a405af0e3..311c60c01 100644 --- a/tests/eko/test_ev_operator.py +++ b/tests/eko/test_ev_operator.py @@ -13,8 +13,8 @@ from eko.evolution_operator import Operator, quad_ker from eko.evolution_operator.grid import OperatorGrid from eko.interpolation import InterpolatorDispatcher, XGrid -from eko.kernels import QEDnon_singlet as qed_ns from eko.kernels import non_singlet as ns +from eko.kernels import non_singlet_qed as qed_ns from eko.kernels import singlet as s from eko.kernels import utils from eko.thresholds import ThresholdsAtlas diff --git a/tests/eko/test_kernels_QEDns.py b/tests/eko/test_kernels_QEDns.py index 24b48c3e2..5b67cea9c 100644 --- a/tests/eko/test_kernels_QEDns.py +++ b/tests/eko/test_kernels_QEDns.py @@ -7,7 +7,7 @@ from eko import anomalous_dimensions as ad from eko import basis_rotation as br from eko import beta -from eko.kernels import QEDnon_singlet as ns +from eko.kernels import non_singlet_qed as ns methods = [ # "iterate-expanded", diff --git a/tests/eko/test_kernels_QEDsinglet.py b/tests/eko/test_kernels_QEDsinglet.py index 47adb089e..b34e673a0 100644 --- a/tests/eko/test_kernels_QEDsinglet.py +++ b/tests/eko/test_kernels_QEDsinglet.py @@ -5,7 +5,7 @@ import pytest from eko import anomalous_dimensions as ad -from eko.kernels import QEDsinglet as s +from eko.kernels import singlet_qed as s methods = [ # "iterate-expanded", diff --git a/tests/eko/test_kernels_QEDvalence.py b/tests/eko/test_kernels_QEDvalence.py index ee6136e04..af6e1d436 100644 --- a/tests/eko/test_kernels_QEDvalence.py +++ b/tests/eko/test_kernels_QEDvalence.py @@ -5,7 +5,7 @@ import pytest from eko import anomalous_dimensions as ad -from eko.kernels import QEDvalence as val +from eko.kernels import valence_qed as val methods = [ # "iterate-expanded", From 13c31c30851a9ec7bc1226b0b50f293ed3a1143b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 4 Nov 2022 11:07:13 +0100 Subject: [PATCH 230/312] Remove commented function --- src/eko/kernels/non_singlet_qed.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/eko/kernels/non_singlet_qed.py b/src/eko/kernels/non_singlet_qed.py index 69f78e579..4fff9fd30 100644 --- a/src/eko/kernels/non_singlet_qed.py +++ b/src/eko/kernels/non_singlet_qed.py @@ -351,14 +351,5 @@ def running_alphaem_exact(order, gamma_ns, a1, a0, aem_list, nf, ev_op_iteration res *= nnlo_aem2_exact( gamma_ns, a_steps[step], a_steps[step - 1], aem_list[step - 1], nf ) - # res = np.prod( - # [ - # nnlo_aem2_exact( - # gamma_ns, a_steps[i], - # a_steps[i-1], aem_list[step], - # nf - # ) for i in range(1, 1 + ev_op_iterations) - # ] - # ) return res raise NotImplementedError("Selected order is not implemented") From aea20021d76894171dc651e401e62150dc9871c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 4 Nov 2022 11:10:49 +0100 Subject: [PATCH 231/312] Rename lo_aem1_exact -> as1_aem1_exact and same for the others --- src/eko/kernels/non_singlet_qed.py | 36 +++++++++++++++--------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/eko/kernels/non_singlet_qed.py b/src/eko/kernels/non_singlet_qed.py index 4fff9fd30..b0c925799 100644 --- a/src/eko/kernels/non_singlet_qed.py +++ b/src/eko/kernels/non_singlet_qed.py @@ -9,7 +9,7 @@ @nb.njit(cache=True) -def lo_aem1_exact(gamma_ns, a1, a0, aem, nf): # lo refers to the order in as1 +def as1_aem1_exact(gamma_ns, a1, a0, aem, nf): """ O(as1aem1) non-singlet exact EKO. @@ -38,7 +38,7 @@ def lo_aem1_exact(gamma_ns, a1, a0, aem, nf): # lo refers to the order in as1 @nb.njit(cache=True) -def lo_aem2_exact(gamma_ns, a1, a0, aem, nf): # lo refers to the order in as1 +def as1_aem2_exact(gamma_ns, a1, a0, aem, nf): """ O(as1aem2) non-singlet exact EKO. @@ -67,7 +67,7 @@ def lo_aem2_exact(gamma_ns, a1, a0, aem, nf): # lo refers to the order in as1 @nb.njit(cache=True) -def nlo_aem1_exact(gamma_ns, a1, a0, aem, nf): +def as2_aem1_exact(gamma_ns, a1, a0, aem, nf): """ O(as2aem1) non-singlet exact EKO. @@ -97,7 +97,7 @@ def nlo_aem1_exact(gamma_ns, a1, a0, aem, nf): @nb.njit(cache=True) -def nlo_aem2_exact(gamma_ns, a1, a0, aem, nf): +def as2_aem2_exact(gamma_ns, a1, a0, aem, nf): """ O(as2aem2) non-singlet exact EKO. @@ -128,7 +128,7 @@ def nlo_aem2_exact(gamma_ns, a1, a0, aem, nf): @nb.njit(cache=True) -def nnlo_aem1_exact(gamma_ns, a1, a0, aem, nf): +def as3_aem1_exact(gamma_ns, a1, a0, aem, nf): """ O(as3aem1) non-singlet exact EKO. @@ -159,7 +159,7 @@ def nnlo_aem1_exact(gamma_ns, a1, a0, aem, nf): @nb.njit(cache=True) -def nnlo_aem2_exact(gamma_ns, a1, a0, aem, nf): +def as3_aem2_exact(gamma_ns, a1, a0, aem, nf): """ O(as3aem2) non-singlet exact EKO. @@ -268,18 +268,18 @@ def fixed_alphaem_exact(order, gamma_ns, a1, a0, aem, nf): """ if order[1] == 1: if order[0] == 1: - return lo_aem1_exact(gamma_ns, a1, a0, aem, nf) + return as1_aem1_exact(gamma_ns, a1, a0, aem, nf) if order[0] == 2: - return nlo_aem1_exact(gamma_ns, a1, a0, aem, nf) + return as2_aem1_exact(gamma_ns, a1, a0, aem, nf) if order[0] == 3: - return nnlo_aem1_exact(gamma_ns, a1, a0, aem, nf) + return as3_aem1_exact(gamma_ns, a1, a0, aem, nf) if order[1] == 2: if order[0] == 1: - return lo_aem2_exact(gamma_ns, a1, a0, aem, nf) + return as1_aem2_exact(gamma_ns, a1, a0, aem, nf) if order[0] == 2: - return nlo_aem2_exact(gamma_ns, a1, a0, aem, nf) + return as2_aem2_exact(gamma_ns, a1, a0, aem, nf) if order[0] == 3: - return nnlo_aem2_exact(gamma_ns, a1, a0, aem, nf) + return as3_aem2_exact(gamma_ns, a1, a0, aem, nf) raise NotImplementedError("Selected order is not implemented") @@ -317,38 +317,38 @@ def running_alphaem_exact(order, gamma_ns, a1, a0, aem_list, nf, ev_op_iteration if order[1] == 1: if order[0] == 1: for step in range(1, ev_op_iterations + 1): - res *= lo_aem1_exact( + res *= as1_aem1_exact( gamma_ns, a_steps[step], a_steps[step - 1], aem_list[step - 1], nf ) return res if order[0] == 2: for step in range(1, ev_op_iterations + 1): - res *= nlo_aem1_exact( + res *= as2_aem1_exact( gamma_ns, a_steps[step], a_steps[step - 1], aem_list[step - 1], nf ) return res if order[0] == 3: for step in range(1, ev_op_iterations + 1): - res *= nnlo_aem1_exact( + res *= as3_aem1_exact( gamma_ns, a_steps[step], a_steps[step - 1], aem_list[step - 1], nf ) return res if order[1] == 2: if order[0] == 1: for step in range(1, ev_op_iterations + 1): - res *= lo_aem2_exact( + res *= as1_aem2_exact( gamma_ns, a_steps[step], a_steps[step - 1], aem_list[step - 1], nf ) return res if order[0] == 2: for step in range(1, ev_op_iterations + 1): - res *= nlo_aem2_exact( + res *= as2_aem2_exact( gamma_ns, a_steps[step], a_steps[step - 1], aem_list[step - 1], nf ) return res if order[0] == 3: for step in range(1, ev_op_iterations + 1): - res *= nnlo_aem2_exact( + res *= as3_aem2_exact( gamma_ns, a_steps[step], a_steps[step - 1], aem_list[step - 1], nf ) return res From 9b61d4dafaa4423444f68c32b07824714a78bde1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 4 Nov 2022 12:17:51 +0100 Subject: [PATCH 232/312] Change docstring in exp_matrix_2D --- src/eko/anomalous_dimensions/__init__.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index bd1178702..248c7bb56 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -28,29 +28,27 @@ @nb.njit(cache=True) def exp_matrix_2D(gamma): r""" - Compute the exponential and the eigensystem of the singlet anomalous dimension matrix. + Compute the exponential and the eigensystem of a 2D matrix. Parameters ---------- gamma : numpy.ndarray - singlet anomalous dimension matrix + 2D matrix Returns ------- exp : numpy.ndarray - exponential of the singlet anomalous dimension matrix :math:`\gamma_{S}(N)` + exponential of the 2D matrix :math:`\gamma(N)` lambda_p : complex - positive eigenvalue of the singlet anomalous dimension matrix - :math:`\gamma_{S}(N)` + positive eigenvalue of the 2D matrix + :math:`\gamma(N)` lambda_m : complex - negative eigenvalue of the singlet anomalous dimension matrix - :math:`\gamma_{S}(N)` + negative eigenvalue of the 2D matrix + :math:`\gamma(N)` e_p : numpy.ndarray - projector for the positive eigenvalue of the singlet anomalous - dimension matrix :math:`\gamma_{S}(N)` + projector for the positive eigenvalue of the 2D matrix :math:`\gamma(N)` e_m : numpy.ndarray - projector for the negative eigenvalue of the singlet anomalous - dimension matrix :math:`\gamma_{S}(N)` + projector for the negative eigenvalue of the 2D matrix :math:`\gamma(N)` See Also -------- From 12a58884250571c80ea5514a2ad963ece6dc3ee8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 4 Nov 2022 15:40:20 +0100 Subject: [PATCH 233/312] remove else from choose_ns_ad_aem1 --- src/eko/anomalous_dimensions/__init__.py | 6 ++---- tests/eko/test_ad.py | 2 -- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index 248c7bb56..93ff6b9f5 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -91,13 +91,13 @@ def exp_matrix(gamma): projectors on the eigenspaces of the matrix gamma :math:`\gamma(N)` """ w, v = np.linalg.eig(gamma) - tmp = np.linalg.inv(v) + v_inv = np.linalg.inv(v) dim = gamma.shape[0] e = np.zeros((dim, dim, dim), np.complex_) exp = np.zeros((dim, dim), np.complex_) # TODO check if this loop can be entirely cast to numpy for i in range(dim): - e[i] = np.outer(v[:, i], tmp[i]) + e[i] = np.outer(v[:, i], v_inv[i]) exp += e[i] * np.exp(w[i]) return exp, w, e @@ -334,8 +334,6 @@ def choose_ns_ad_aem1(mode, n, sx): return constants.eu2 * aem1.gamma_ns(n, sx) if mode in [10103, 10203]: return constants.ed2 * aem1.gamma_ns(n, sx) - else: - raise NotImplementedError("Non-singlet sector is not implemented") @nb.njit(cache=True) diff --git a/tests/eko/test_ad.py b/tests/eko/test_ad.py index b23fc87ee..ef1af901c 100644 --- a/tests/eko/test_ad.py +++ b/tests/eko/test_ad.py @@ -224,7 +224,5 @@ def test_dim_nsp(): assert gamma_nsup.shape == (4, 3) gamma_nsdp = ad.gamma_ns_qed((3, 2), 10103, N, nf) assert gamma_nsdp.shape == (4, 3) - with pytest.raises(NotImplementedError): - gamma_ns = ad.gamma_ns_qed((1, 1), 10106, N, nf) with pytest.raises(NotImplementedError): gamma_ns = ad.gamma_ns_qed((2, 0), 10106, N, nf) From f7a38935cbaaac98b696416f4d6a0adc6668edab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 4 Nov 2022 15:42:04 +0100 Subject: [PATCH 234/312] Rename as1_aem1_exact -> as1aem1_exact and same for the others --- src/eko/kernels/non_singlet_qed.py | 36 +++++++++++++++--------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/eko/kernels/non_singlet_qed.py b/src/eko/kernels/non_singlet_qed.py index b0c925799..6b14cbf8f 100644 --- a/src/eko/kernels/non_singlet_qed.py +++ b/src/eko/kernels/non_singlet_qed.py @@ -9,7 +9,7 @@ @nb.njit(cache=True) -def as1_aem1_exact(gamma_ns, a1, a0, aem, nf): +def as1aem1_exact(gamma_ns, a1, a0, aem, nf): """ O(as1aem1) non-singlet exact EKO. @@ -38,7 +38,7 @@ def as1_aem1_exact(gamma_ns, a1, a0, aem, nf): @nb.njit(cache=True) -def as1_aem2_exact(gamma_ns, a1, a0, aem, nf): +def as1aem2_exact(gamma_ns, a1, a0, aem, nf): """ O(as1aem2) non-singlet exact EKO. @@ -67,7 +67,7 @@ def as1_aem2_exact(gamma_ns, a1, a0, aem, nf): @nb.njit(cache=True) -def as2_aem1_exact(gamma_ns, a1, a0, aem, nf): +def as2aem1_exact(gamma_ns, a1, a0, aem, nf): """ O(as2aem1) non-singlet exact EKO. @@ -97,7 +97,7 @@ def as2_aem1_exact(gamma_ns, a1, a0, aem, nf): @nb.njit(cache=True) -def as2_aem2_exact(gamma_ns, a1, a0, aem, nf): +def as2aem2_exact(gamma_ns, a1, a0, aem, nf): """ O(as2aem2) non-singlet exact EKO. @@ -128,7 +128,7 @@ def as2_aem2_exact(gamma_ns, a1, a0, aem, nf): @nb.njit(cache=True) -def as3_aem1_exact(gamma_ns, a1, a0, aem, nf): +def as3aem1_exact(gamma_ns, a1, a0, aem, nf): """ O(as3aem1) non-singlet exact EKO. @@ -159,7 +159,7 @@ def as3_aem1_exact(gamma_ns, a1, a0, aem, nf): @nb.njit(cache=True) -def as3_aem2_exact(gamma_ns, a1, a0, aem, nf): +def as3aem2_exact(gamma_ns, a1, a0, aem, nf): """ O(as3aem2) non-singlet exact EKO. @@ -268,18 +268,18 @@ def fixed_alphaem_exact(order, gamma_ns, a1, a0, aem, nf): """ if order[1] == 1: if order[0] == 1: - return as1_aem1_exact(gamma_ns, a1, a0, aem, nf) + return as1aem1_exact(gamma_ns, a1, a0, aem, nf) if order[0] == 2: - return as2_aem1_exact(gamma_ns, a1, a0, aem, nf) + return as2aem1_exact(gamma_ns, a1, a0, aem, nf) if order[0] == 3: - return as3_aem1_exact(gamma_ns, a1, a0, aem, nf) + return as3aem1_exact(gamma_ns, a1, a0, aem, nf) if order[1] == 2: if order[0] == 1: - return as1_aem2_exact(gamma_ns, a1, a0, aem, nf) + return as1aem2_exact(gamma_ns, a1, a0, aem, nf) if order[0] == 2: - return as2_aem2_exact(gamma_ns, a1, a0, aem, nf) + return as2aem2_exact(gamma_ns, a1, a0, aem, nf) if order[0] == 3: - return as3_aem2_exact(gamma_ns, a1, a0, aem, nf) + return as3aem2_exact(gamma_ns, a1, a0, aem, nf) raise NotImplementedError("Selected order is not implemented") @@ -317,38 +317,38 @@ def running_alphaem_exact(order, gamma_ns, a1, a0, aem_list, nf, ev_op_iteration if order[1] == 1: if order[0] == 1: for step in range(1, ev_op_iterations + 1): - res *= as1_aem1_exact( + res *= as1aem1_exact( gamma_ns, a_steps[step], a_steps[step - 1], aem_list[step - 1], nf ) return res if order[0] == 2: for step in range(1, ev_op_iterations + 1): - res *= as2_aem1_exact( + res *= as2aem1_exact( gamma_ns, a_steps[step], a_steps[step - 1], aem_list[step - 1], nf ) return res if order[0] == 3: for step in range(1, ev_op_iterations + 1): - res *= as3_aem1_exact( + res *= as3aem1_exact( gamma_ns, a_steps[step], a_steps[step - 1], aem_list[step - 1], nf ) return res if order[1] == 2: if order[0] == 1: for step in range(1, ev_op_iterations + 1): - res *= as1_aem2_exact( + res *= as1aem2_exact( gamma_ns, a_steps[step], a_steps[step - 1], aem_list[step - 1], nf ) return res if order[0] == 2: for step in range(1, ev_op_iterations + 1): - res *= as2_aem2_exact( + res *= as2aem2_exact( gamma_ns, a_steps[step], a_steps[step - 1], aem_list[step - 1], nf ) return res if order[0] == 3: for step in range(1, ev_op_iterations + 1): - res *= as3_aem2_exact( + res *= as3aem2_exact( gamma_ns, a_steps[step], a_steps[step - 1], aem_list[step - 1], nf ) return res From 64b0de17fbda4f80be618b96e40b7e4ff9a761f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 4 Nov 2022 15:52:55 +0100 Subject: [PATCH 235/312] Further simplify exp_matrix --- src/eko/anomalous_dimensions/__init__.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index 93ff6b9f5..4c256895e 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -96,9 +96,10 @@ def exp_matrix(gamma): e = np.zeros((dim, dim, dim), np.complex_) exp = np.zeros((dim, dim), np.complex_) # TODO check if this loop can be entirely cast to numpy - for i in range(dim): - e[i] = np.outer(v[:, i], v_inv[i]) - exp += e[i] * np.exp(w[i]) + # for i in range(dim): + # e[i] = np.outer(v[:, i], v_inv[i]) + # exp += e[i] * np.exp(w[i]) + exp = np.einsum("ij,j,jk->ik", v, np.exp(w), v_inv) return exp, w, e From 1092f99b47fa8e4097d420dcabc9f5f3f7e97fd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Sat, 5 Nov 2022 17:50:47 +0100 Subject: [PATCH 236/312] Revert "Further simplify exp_matrix" This reverts commit 64b0de17fbda4f80be618b96e40b7e4ff9a761f7. --- src/eko/anomalous_dimensions/__init__.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index 4c256895e..93ff6b9f5 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -96,10 +96,9 @@ def exp_matrix(gamma): e = np.zeros((dim, dim, dim), np.complex_) exp = np.zeros((dim, dim), np.complex_) # TODO check if this loop can be entirely cast to numpy - # for i in range(dim): - # e[i] = np.outer(v[:, i], v_inv[i]) - # exp += e[i] * np.exp(w[i]) - exp = np.einsum("ij,j,jk->ik", v, np.exp(w), v_inv) + for i in range(dim): + e[i] = np.outer(v[:, i], v_inv[i]) + exp += e[i] * np.exp(w[i]) return exp, w, e From d3281df1524e0468f9f6ace0eda324af7a7b450f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 14 Nov 2022 12:01:22 +0100 Subject: [PATCH 237/312] Fix typo in extras/qed_matching/matching_and_rotation.tex --- extras/qed_matching/matching_and_rotation.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extras/qed_matching/matching_and_rotation.tex b/extras/qed_matching/matching_and_rotation.tex index 79528902c..2c5b6f04c 100644 --- a/extras/qed_matching/matching_and_rotation.tex +++ b/extras/qed_matching/matching_and_rotation.tex @@ -103,7 +103,7 @@ \subsection{$\Sigma_{\Delta}$} In the end we find that \begin{align*} -\Sigma_{\Delta(n_f+1)} &= \Bigl(\frac{n_d(n_f+1)}{n_u(n_f+1)}n_u(n_f)-n_d(n_f)\Bigr)\Sigma_{(n_f)} + \frac{n_f+1}{n_u(n_f+1)}\frac{n_u(n_f)}{n_f}\Sigma_{\Delta(n_f)} + k(n_f) h_+ +\Sigma_{\Delta(n_f+1)} &= \frac{1}{n_f}\Bigl(\frac{n_d(n_f+1)}{n_u(n_f+1)}n_u(n_f)-n_d(n_f)\Bigr)\Sigma_{(n_f)} + \frac{n_f+1}{n_u(n_f+1)}\frac{n_u(n_f)}{n_f}\Sigma_{\Delta(n_f)} + k(n_f) h_+ \end{align*} \subsection{$T_i$} From 285d65a1f369f05dbc6ebea5af689e9e7fae949b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 14 Nov 2022 18:28:12 +0100 Subject: [PATCH 238/312] Add alphaem_running to runcard --- benchmarks/CT18_bench.py | 1 + benchmarks/NNPDF_bench.py | 5 +---- benchmarks/apfel_bench.py | 2 ++ src/eko/compatibility.py | 1 - 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/benchmarks/CT18_bench.py b/benchmarks/CT18_bench.py index 714454cd4..980f10849 100644 --- a/benchmarks/CT18_bench.py +++ b/benchmarks/CT18_bench.py @@ -65,6 +65,7 @@ def benchmark_nnlo_qed(self, Q0=1.295, Q2grid=(1e4,)): "Q0": Q0, "MaxNfPdf": 5, "MaxNfAs": 5, + "alphaem_running": True, } ) operator_card = {"Q2grid": list(Q2grid)} diff --git a/benchmarks/NNPDF_bench.py b/benchmarks/NNPDF_bench.py index 1d686d452..b5ca890bb 100644 --- a/benchmarks/NNPDF_bench.py +++ b/benchmarks/NNPDF_bench.py @@ -74,10 +74,7 @@ def benchmark_nnlo(self, Q0=1.65, Q2grid=(100,)): "Q0": Q0, } theory_card.update( - { - "ModEv": "iterate-exact", - "FNS": "VFNS", - } + {"ModEv": "iterate-exact", "FNS": "VFNS", "alphaem_running": True} ) self.skip_pdfs = lambda _theory: [ diff --git a/benchmarks/apfel_bench.py b/benchmarks/apfel_bench.py index 4bc5c2397..2cdeef887 100644 --- a/benchmarks/apfel_bench.py +++ b/benchmarks/apfel_bench.py @@ -209,6 +209,7 @@ class BenchmarkFFNS_qed(ApfelBenchmark): "Q0": 5.0, "alphas": 0.118000, "alphaqed": 0.007496, + "alphaem_running": True, } ffns_theory = tolist(ffns_theory) @@ -286,6 +287,7 @@ class BenchmarkVFNS_qed(ApfelBenchmark): "Q0": 1.25, "alphas": 0.118000, "alphaqed": 0.007496, + "alphaem_running": True, } vfns_theory = tolist(vfns_theory) diff --git a/src/eko/compatibility.py b/src/eko/compatibility.py index 2d66306db..2aedc5418 100644 --- a/src/eko/compatibility.py +++ b/src/eko/compatibility.py @@ -35,7 +35,6 @@ def update(theory: dict, operators: Optional[dict]): new_theory["order"] = [new_theory.pop("PTO") + 1, new_theory.pop("QED")] if "alphaem_running" not in new_theory: new_theory["alphaem_running"] = False - # TODO : add alphaem_running to the runcard if operators is not None and "configs" not in operators: new_operators["configs"] = {} From 476211e1a07ee8cf03abe6e821dd0bf4e74e9ddd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 28 Nov 2022 21:53:11 +0100 Subject: [PATCH 239/312] Fix typos --- src/eko/anomalous_dimensions/__init__.py | 1 - src/eko/scale_variations/expanded.py | 4 ---- 2 files changed, 5 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index d99dacf28..1dab7962a 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -476,7 +476,6 @@ def gamma_valence_qed(order, n, nf): """ # cache the s-es max_weight = max(order) - max_weight = max(order) if max_weight >= 3: # here we need only S1,S2,S3,S4 sx = harmonics.sx(n, max_weight=max_weight + 1) diff --git a/src/eko/scale_variations/expanded.py b/src/eko/scale_variations/expanded.py index 4c81ebde2..40f86df40 100644 --- a/src/eko/scale_variations/expanded.py +++ b/src/eko/scale_variations/expanded.py @@ -307,8 +307,6 @@ def QEDvalence_variation(gamma, a_s, a_em, alphaem_running, order, nf, L): sv_ker = np.eye(2, dtype=np.complex_) gamma = np.ascontiguousarray(gamma) if not alphaem_running: - sv_ker = np.eye(2, dtype=np.complex_) - gamma = np.ascontiguousarray(gamma) if order[0] >= 2: sv_ker += a_s * variation_as1(gamma[1:, 0], L) if order[0] >= 3: @@ -325,8 +323,6 @@ def QEDvalence_variation(gamma, a_s, a_em, alphaem_running, order, nf, L): gamma[1:, 0], L, beta0, beta1, gamma10e2, gamma10e3, g20g10, g10g20 ) else: - sv_ker = np.eye(2, dtype=np.complex_) - gamma = np.ascontiguousarray(gamma) if order[0] >= 2: sv_ker += a_s * variation_as1(gamma[1:, 0], L) if order[1] >= 2: From f166988301604688acb0226a66524265206f355d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 28 Nov 2022 22:03:44 +0100 Subject: [PATCH 240/312] Refactor expanded scale variations for QED --- src/eko/scale_variations/expanded.py | 122 ++++++++------------------- 1 file changed, 35 insertions(+), 87 deletions(-) diff --git a/src/eko/scale_variations/expanded.py b/src/eko/scale_variations/expanded.py index 40f86df40..091862a57 100644 --- a/src/eko/scale_variations/expanded.py +++ b/src/eko/scale_variations/expanded.py @@ -194,31 +194,11 @@ def QEDnon_singlet_variation(gamma, a_s, a_em, alphaem_running, order, nf, L): complex scale variation kernel """ - if not alphaem_running: - return non_singlet_variation(gamma[1:, 0], a_s, order, nf, L) - else: - sv_ker = 1.0 - if order[0] >= 2: - sv_ker += a_s * variation_as1(gamma[1:, 0], L) + sv_ker = non_singlet_variation(gamma[1:, 0], a_s, order, nf, L) + if alphaem_running: if order[1] >= 2: sv_ker += a_em * variation_as1(gamma[0, 1:], L) - if order[0] >= 3: - beta0 = beta.beta_qcd_as2(nf) - sv_ker += a_s**2 * variation_as2(gamma[1:, 0], L, beta0, gamma[1, 0] ** 2) - if order[0] >= 4: - beta1 = beta.beta_qcd((3, 0), nf) - g0g1 = gamma[1, 0] * gamma[2, 0] - sv_ker += a_s**3 * variation_as3( - gamma[1:, 0], - L, - beta0, - beta1, - gamma[1, 0] ** 2, - gamma[1, 0] ** 3, - g0g1, - g0g1, - ) - return sv_ker + return sv_ker @nb.njit(cache=True) @@ -245,40 +225,24 @@ def QEDsinglet_variation(gamma, a_s, a_em, alphaem_running, order, nf, L): """ sv_ker = np.eye(4, dtype=np.complex_) gamma = np.ascontiguousarray(gamma) - if not alphaem_running: - if order[0] >= 2: - sv_ker += a_s * variation_as1(gamma[1:, 0], L) - if order[0] >= 3: - beta0 = beta.beta_qcd_as2(nf) - gamma10e2 = gamma[1, 0] @ gamma[1, 0] - sv_ker += a_s**2 * variation_as2(gamma[1:, 0], L, beta0, gamma10e2) - if order[0] >= 4: - beta1 = beta.beta_qcd((3, 0), nf) - gamma10e3 = gamma10e2 @ gamma[1, 0] - # here the product is not commutative - g20g10 = gamma[2, 0] @ gamma[1, 0] - g10g20 = gamma[1, 0] @ gamma[2, 0] - sv_ker += a_s**3 * variation_as3( - gamma[1:, 0], L, beta0, beta1, gamma10e2, gamma10e3, g20g10, g10g20 - ) - else: - if order[0] >= 2: - sv_ker += a_s * variation_as1(gamma[1:, 0], L) + if order[0] >= 2: + sv_ker += a_s * variation_as1(gamma[1:, 0], L) + if order[0] >= 3: + beta0 = beta.beta_qcd_as2(nf) + gamma10e2 = gamma[1, 0] @ gamma[1, 0] + sv_ker += a_s**2 * variation_as2(gamma[1:, 0], L, beta0, gamma10e2) + if order[0] >= 4: + beta1 = beta.beta_qcd((3, 0), nf) + gamma10e3 = gamma10e2 @ gamma[1, 0] + # here the product is not commutative + g20g10 = gamma[2, 0] @ gamma[1, 0] + g10g20 = gamma[1, 0] @ gamma[2, 0] + sv_ker += a_s**3 * variation_as3( + gamma[1:, 0], L, beta0, beta1, gamma10e2, gamma10e3, g20g10, g10g20 + ) + if alphaem_running: if order[1] >= 2: sv_ker += a_em * variation_as1(gamma[0, 1:], L) - if order[0] >= 3: - beta0 = beta.beta_qcd_as2(nf) - gamma10e2 = gamma[1, 0] @ gamma[1, 0] - sv_ker += a_s**2 * variation_as2(gamma[1:, 0], L, beta0, gamma10e2) - if order[0] >= 4: - beta1 = beta.beta_qcd((3, 0), nf) - gamma10e3 = gamma10e2 @ gamma[1, 0] - # here the product is not commutative - g20g10 = gamma[2, 0] @ gamma[1, 0] - g10g20 = gamma[1, 0] @ gamma[2, 0] - sv_ker += a_s**3 * variation_as3( - gamma[1:, 0], L, beta0, beta1, gamma10e2, gamma10e3, g20g10, g10g20 - ) return sv_ker @@ -306,38 +270,22 @@ def QEDvalence_variation(gamma, a_s, a_em, alphaem_running, order, nf, L): """ sv_ker = np.eye(2, dtype=np.complex_) gamma = np.ascontiguousarray(gamma) - if not alphaem_running: - if order[0] >= 2: - sv_ker += a_s * variation_as1(gamma[1:, 0], L) - if order[0] >= 3: - beta0 = beta.beta_qcd_as2(nf) - gamma10e2 = gamma[1, 0] @ gamma[1, 0] - sv_ker += a_s**2 * variation_as2(gamma[1:, 0], L, beta0, gamma10e2) - if order[0] >= 4: - beta1 = beta.beta_qcd((3, 0), nf) - gamma10e3 = gamma10e2 @ gamma[1, 0] - # here the product is not commutative - g20g10 = gamma[2, 0] @ gamma[1, 0] - g10g20 = gamma[1, 0] @ gamma[2, 0] - sv_ker += a_s**3 * variation_as3( - gamma[1:, 0], L, beta0, beta1, gamma10e2, gamma10e3, g20g10, g10g20 - ) - else: - if order[0] >= 2: - sv_ker += a_s * variation_as1(gamma[1:, 0], L) + if order[0] >= 2: + sv_ker += a_s * variation_as1(gamma[1:, 0], L) + if order[0] >= 3: + beta0 = beta.beta_qcd_as2(nf) + gamma10e2 = gamma[1, 0] @ gamma[1, 0] + sv_ker += a_s**2 * variation_as2(gamma[1:, 0], L, beta0, gamma10e2) + if order[0] >= 4: + beta1 = beta.beta_qcd((3, 0), nf) + gamma10e3 = gamma10e2 @ gamma[1, 0] + # here the product is not commutative + g20g10 = gamma[2, 0] @ gamma[1, 0] + g10g20 = gamma[1, 0] @ gamma[2, 0] + sv_ker += a_s**3 * variation_as3( + gamma[1:, 0], L, beta0, beta1, gamma10e2, gamma10e3, g20g10, g10g20 + ) + if alphaem_running: if order[1] >= 2: sv_ker += a_em * variation_as1(gamma[0, 1:], L) - if order[0] >= 3: - beta0 = beta.beta_qcd_as2(nf) - gamma10e2 = gamma[1, 0] @ gamma[1, 0] - sv_ker += a_s**2 * variation_as2(gamma[1:, 0], L, beta0, gamma10e2) - if order[0] >= 4: - beta1 = beta.beta_qcd((3, 0), nf) - gamma10e3 = gamma10e2 @ gamma[1, 0] - # here the product is not commutative - g20g10 = gamma[2, 0] @ gamma[1, 0] - g10g20 = gamma[1, 0] @ gamma[2, 0] - sv_ker += a_s**3 * variation_as3( - gamma[1:, 0], L, beta0, beta1, gamma10e2, gamma10e3, g20g10, g10g20 - ) return sv_ker From a86477207552bfd4e10f341b32a268d71fb7e87f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 28 Nov 2022 22:20:45 +0100 Subject: [PATCH 241/312] Refactor exponentiated QED scale var --- src/eko/scale_variations/exponentiated.py | 40 ++++------------------- 1 file changed, 6 insertions(+), 34 deletions(-) diff --git a/src/eko/scale_variations/exponentiated.py b/src/eko/scale_variations/exponentiated.py index 58d78a1a9..6dc1f2935 100644 --- a/src/eko/scale_variations/exponentiated.py +++ b/src/eko/scale_variations/exponentiated.py @@ -66,40 +66,12 @@ def gamma_variation_qed(gamma, order, nf, L, alphaem_running): gamma : numpy.ndarray adjusted anomalous dimensions """ - if not alphaem_running: - # if alphaem is fixed then only alphas is varied so gamma[0,1] and gamma[0,2] - # don't get a variation while gamma[1,1] gets a variation that is O(as2aem1) - # that we are neglecting - gamma[1:, 0] = gamma_variation(gamma[1:, 0], order, nf, L) - return gamma - else: - beta0qcd = beta.beta_qcd((2, 0), nf) - beta1qcd = beta.beta_qcd((3, 0), nf) - beta2qcd = beta.beta_qcd((4, 0), nf) - beta0qed = beta.beta_qed((0, 2), nf) - # beta1qed = beta.beta_qcd((0, 3), nf) - # beta01qcd = beta.beta_qcd((2, 1), nf) - if order[0] >= 4: - gamma[4, 0] -= ( - 3 * beta0qcd * L * gamma[3, 0] - + (2 * beta1qcd * L - 3 * beta0qcd**2 * L**2) * gamma[2, 0] - + ( - beta2qcd * L - - 5 / 2 * beta1qcd * beta0qcd * L**2 - + beta0qcd**3 * L**3 - ) - * gamma[1, 0] - ) - if order[0] >= 3: - gamma[3, 0] -= ( - 2 * beta0qcd * gamma[2, 0] * L - + (beta1qcd * L - beta0qcd**2 * L**2) * gamma[1, 0] - ) - if order[0] >= 2: - gamma[2, 0] -= beta0qcd * gamma[1, 0] * L - # we are neglecting the order (2,1) - # if order[1] >= 1: - # gamma[2, 1] -= -L * (beta01qcd * gamma[1,0] + beta0qcd * gamma[1,1]) + # if alphaem is fixed then only alphas is varied so gamma[0,1] and gamma[0,2] + # don't get a variation while gamma[1,1] gets a variation that is O(as2aem1) + # that we are neglecting + gamma[1:, 0] = gamma_variation(gamma[1:, 0], order, nf, L) + if alphaem_running: if order[1] >= 2: + beta0qed = beta.beta_qed((0, 2), nf) gamma[0, 2] -= beta0qed * gamma[0, 1] * L return gamma From ad485094aa1172e418d2eb502f71ff90704bcf69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 28 Nov 2022 22:23:51 +0100 Subject: [PATCH 242/312] Change raise in singlet_qed and valence_qed --- src/eko/kernels/singlet_qed.py | 3 +-- src/eko/kernels/valence_qed.py | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/eko/kernels/singlet_qed.py b/src/eko/kernels/singlet_qed.py index 13dd62b0f..18a38e875 100644 --- a/src/eko/kernels/singlet_qed.py +++ b/src/eko/kernels/singlet_qed.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """Collection of QED singlet EKOs.""" import numba as nb import numpy as np @@ -100,4 +99,4 @@ def dispatcher( # pylint: disable=too-many-return-statements """ if method in ["iterate-exact", "iterate-expanded"]: return eko_iterate(gamma_singlet, a1, a0, aem_list, nf, order, ev_op_iterations) - raise NotImplementedError("Selected method is not implemented") + raise NotImplementedError('Only "iterate-exact" is implemented with QED') diff --git a/src/eko/kernels/valence_qed.py b/src/eko/kernels/valence_qed.py index b3863bf43..b090d7e41 100644 --- a/src/eko/kernels/valence_qed.py +++ b/src/eko/kernels/valence_qed.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """Collection of QED valence EKOs.""" import numba as nb import numpy as np @@ -103,4 +102,4 @@ def dispatcher( # pylint: disable=too-many-return-statements """ if method in ["iterate-exact", "iterate-expanded"]: return eko_iterate(gamma_valence, a1, a0, aem_list, nf, order, ev_op_iterations) - raise NotImplementedError("Selected method is not implemented") + raise NotImplementedError('Only "iterate-exact" is implemented with QED') From 9cf11e6a82b1c23c4a93c733914d7651005aef69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 29 Nov 2022 09:57:43 +0100 Subject: [PATCH 243/312] Remove if condition --- src/eko/anomalous_dimensions/__init__.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index 1dab7962a..309b02b0c 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -280,8 +280,7 @@ def gamma_ns_qed(order, mode, n, nf): sx = harmonics.sx(n, max_weight=max_weight + 1) else: sx = harmonics.sx(n, max_weight=3) - if order[1] >= 1: - sx_ns_qed = harmonics.compute_qed_ns_cache(n, sx[0]) + sx_ns_qed = harmonics.compute_qed_ns_cache(n, sx[0]) # now combine gamma_ns = np.zeros((order[0] + 1, order[1] + 1), np.complex_) if order[0] >= 1: @@ -428,8 +427,7 @@ def gamma_singlet_qed(order, n, nf): sx = harmonics.sx(n, max_weight=max_weight + 1) else: sx = harmonics.sx(n, max_weight=3) - if order[1] >= 1: - sx_ns_qed = harmonics.compute_qed_ns_cache(n, sx[0]) + sx_ns_qed = harmonics.compute_qed_ns_cache(n, sx[0]) gamma_s = np.zeros((order[0] + 1, order[1] + 1, 4, 4), np.complex_) if order[0] >= 1: gamma_s[1, 0] = as1.gamma_QEDsinglet(n, sx[0], nf) @@ -481,8 +479,7 @@ def gamma_valence_qed(order, n, nf): sx = harmonics.sx(n, max_weight=max_weight + 1) else: sx = harmonics.sx(n, max_weight=3) - if order[1] >= 1: - sx_ns_qed = harmonics.compute_qed_ns_cache(n, sx[0]) + sx_ns_qed = harmonics.compute_qed_ns_cache(n, sx[0]) gamma_v = np.zeros((order[0] + 1, order[1] + 1, 2, 2), np.complex_) if order[0] >= 1: gamma_v[1, 0] = as1.gamma_QEDvalence(n, sx[0]) From 672be8c86ffa0073dfa7ffaf092d307adc6994bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 29 Nov 2022 10:44:58 +0100 Subject: [PATCH 244/312] Remove unnecessary if --- src/eko/kernels/non_singlet_qed.py | 10 +--------- src/eko/kernels/singlet_qed.py | 2 +- src/eko/kernels/valence_qed.py | 2 +- tests/eko/test_kernels_QEDns.py | 5 ++--- 4 files changed, 5 insertions(+), 14 deletions(-) diff --git a/src/eko/kernels/non_singlet_qed.py b/src/eko/kernels/non_singlet_qed.py index 6b14cbf8f..478f38514 100644 --- a/src/eko/kernels/non_singlet_qed.py +++ b/src/eko/kernels/non_singlet_qed.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """Collection of QED non-singlet EKOs.""" import numba as nb import numpy as np @@ -193,7 +192,7 @@ def as3aem2_exact(gamma_ns, a1, a0, aem, nf): @nb.njit(cache=True) def dispatcher( order, method, gamma_ns, a1, a0, aem_list, alphaem_running, nf, ev_op_iterations -): # pylint: disable=too-many-return-statements +): """ Determine used kernel and call it. @@ -225,13 +224,6 @@ def dispatcher( e_ns : complex non-singlet EKO """ - # use always exact in LO - if order[1] == 0: - return non_singlet.dispatcher( - order, method, gamma_ns[1:, 0], a1, a0, nf, ev_op_iterations - ) - # this if is probably useless since when order[1] == 0 - # the code never enters in this module if not alphaem_running: aem = aem_list[0] return fixed_alphaem_exact(order, gamma_ns, a1, a0, aem, nf) diff --git a/src/eko/kernels/singlet_qed.py b/src/eko/kernels/singlet_qed.py index 18a38e875..ed50dd1d7 100644 --- a/src/eko/kernels/singlet_qed.py +++ b/src/eko/kernels/singlet_qed.py @@ -57,7 +57,7 @@ def eko_iterate(gamma_singlet, a1, a0, aem_list, nf, order, ev_op_iterations): @nb.njit(cache=True) -def dispatcher( # pylint: disable=too-many-return-statements +def dispatcher( order, method, gamma_singlet, diff --git a/src/eko/kernels/valence_qed.py b/src/eko/kernels/valence_qed.py index b090d7e41..f34918fef 100644 --- a/src/eko/kernels/valence_qed.py +++ b/src/eko/kernels/valence_qed.py @@ -58,7 +58,7 @@ def eko_iterate(gamma_valence, a1, a0, aem_list, nf, order, ev_op_iterations): @nb.njit(cache=True) -def dispatcher( # pylint: disable=too-many-return-statements +def dispatcher( order, method, gamma_valence, diff --git a/tests/eko/test_kernels_QEDns.py b/tests/eko/test_kernels_QEDns.py index 5b67cea9c..3a488e7fa 100644 --- a/tests/eko/test_kernels_QEDns.py +++ b/tests/eko/test_kernels_QEDns.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import warnings import numpy as np @@ -27,7 +26,7 @@ def test_zero(): ev_op_iterations = 2 running_alpha = [True, False] for qcd in range(1, 3 + 1): - for qed in range(0, 2 + 1): + for qed in range(1, 2 + 1): order = (qcd, qed) gamma_ns = ( np.random.rand(qcd + 1, qed + 1) + np.random.rand(qcd + 1, qed + 1) * 1j @@ -73,7 +72,7 @@ def test_zero_true_gamma(): if mode in [10201, 10101, 10200]: continue for qcd in range(1, 3 + 1): - for qed in range(0, 2 + 1): + for qed in range(1, 2 + 1): order = (qcd, qed) n = np.random.rand() gamma_ns = ad.gamma_ns_qed(order, mode, n, nf) From 726793f02a4848a34576f074a4b155f0fd32d8fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 29 Nov 2022 10:55:41 +0100 Subject: [PATCH 245/312] Change docstring in the QED gammas --- src/eko/anomalous_dimensions/aem1.py | 2 +- src/eko/anomalous_dimensions/aem2.py | 50 ++++++++++++------------- src/eko/anomalous_dimensions/as1aem1.py | 50 ++++++++++++------------- 3 files changed, 51 insertions(+), 51 deletions(-) diff --git a/src/eko/anomalous_dimensions/aem1.py b/src/eko/anomalous_dimensions/aem1.py index 1a64e42f2..fb0c2a3c0 100644 --- a/src/eko/anomalous_dimensions/aem1.py +++ b/src/eko/anomalous_dimensions/aem1.py @@ -1,4 +1,4 @@ -"""Contains the O(aem1) Altarelli-Parisi splitting kernels.""" +"""The :math:`O(a_{em}^1)` Altarelli-Parisi splitting kernels.""" import numba as nb import numpy as np diff --git a/src/eko/anomalous_dimensions/aem2.py b/src/eko/anomalous_dimensions/aem2.py index 6de6301b4..bb4cbc8f7 100644 --- a/src/eko/anomalous_dimensions/aem2.py +++ b/src/eko/anomalous_dimensions/aem2.py @@ -1,4 +1,4 @@ -"""Contains the O(aem2) Altarelli-Parisi splitting kernels.""" +"""The :math:`O(a_{em}^2)` Altarelli-Parisi splitting kernels.""" import numba as nb import numpy as np @@ -9,7 +9,7 @@ @nb.njit(cache=True) def gamma_phph(N, nf): - r"""Compute the O(aem2) photon-photon singlet anomalous dimension. + r"""Compute the :math:`O(a_{em}^2)` photon-photon singlet anomalous dimension. Implements Eq. (68) of :cite:`deFlorian:2016gvk`. @@ -23,7 +23,7 @@ def gamma_phph(N, nf): Returns ------- gamma_gg : complex - O(aem2) photon-photon singlet anomalous dimension + :math:`O(a_{em}^2)` photon-photon singlet anomalous dimension :math:`\\gamma_{\\gamma \\gamma}^{(0,2)}(N)` """ @@ -38,7 +38,7 @@ def gamma_phph(N, nf): @nb.njit(cache=True) def gamma_uph(N, nf, sx): - r"""Compute the O(aem2) quark-photon anomalous dimension for up quarks. + r"""Compute the :math:`O(a_{em}^2)` quark-photon anomalous dimension for up quarks. Implements Eq. (55) of :cite:`deFlorian:2016gvk` for q=u. @@ -54,7 +54,7 @@ def gamma_uph(N, nf, sx): Returns ------- gamma_uph : complex - O(aem2) quark-photon anomalous dimension :math:`\\gamma_{u \\gamma}^{(0,2)}(N)` + :math:`O(a_{em}^2)` quark-photon anomalous dimension :math:`\\gamma_{u \\gamma}^{(0,2)}(N)` """ return constants.eu2 * as1aem1.gamma_qph(N, nf, sx) / constants.CF @@ -62,7 +62,7 @@ def gamma_uph(N, nf, sx): @nb.njit(cache=True) def gamma_dph(N, nf, sx): - r"""Compute the O(aem2) quark-photon anomalous dimension for down quarks. + r"""Compute the :math:`O(a_{em}^2)` quark-photon anomalous dimension for down quarks. Implements Eq. (55) of :cite:`deFlorian:2016gvk` for q=d. @@ -78,7 +78,7 @@ def gamma_dph(N, nf, sx): Returns ------- gamma_dph : complex - O(aem2) quark-photon anomalous dimension :math:`\\gamma_{d \\gamma}^{(0,2)}(N)` + :math:`O(a_{em}^2)` quark-photon anomalous dimension :math:`\\gamma_{d \\gamma}^{(0,2)}(N)` """ return constants.ed2 * as1aem1.gamma_qph(N, nf, sx) / constants.CF @@ -86,7 +86,7 @@ def gamma_dph(N, nf, sx): @nb.njit(cache=True) def gamma_phu(N, nf, sx): - r"""Compute the O(aem2) photon-quark anomalous dimension for up quarks. + r"""Compute the :math:`O(a_{em}^2)` photon-quark anomalous dimension for up quarks. Implements Eq. (56) of :cite:`deFlorian:2016gvk` for q=u. @@ -102,7 +102,7 @@ def gamma_phu(N, nf, sx): Returns ------- gamma_phu : complex - O(aem2) photon-quark anomalous dimension :math:`\\gamma_{\\gamma u}^{(0,2)}(N)` + :math:`O(a_{em}^2)` photon-quark anomalous dimension :math:`\\gamma_{\\gamma u}^{(0,2)}(N)` """ nu = constants.uplike_flavors(nf) @@ -119,7 +119,7 @@ def gamma_phu(N, nf, sx): @nb.njit(cache=True) def gamma_phd(N, nf, sx): - r"""Compute the O(aem2) photon-quark anomalous dimension for down quarks. + r"""Compute the :math:`O(a_{em}^2)` photon-quark anomalous dimension for down quarks. Implements Eq. (56) of :cite:`deFlorian:2016gvk` for q=d. @@ -135,7 +135,7 @@ def gamma_phd(N, nf, sx): Returns ------- gamma_phd : complex - O(aem2) photon-quark anomalous dimension :math:`\\gamma_{\\gamma d}^{(0,2)}(N)` + :math:`O(a_{em}^2)` photon-quark anomalous dimension :math:`\\gamma_{\\gamma d}^{(0,2)}(N)` """ nu = constants.uplike_flavors(nf) @@ -152,7 +152,7 @@ def gamma_phd(N, nf, sx): @nb.njit(cache=True) def gamma_nspu(N, nf, sx, sx_ns_qed): - r"""Compute the O(aem2) singlet-like non-singlet anomalous dimension for up quarks. + r"""Compute the :math:`O(a_{em}^2)` singlet-like non-singlet anomalous dimension for up quarks. Implements sum of Eqs. (57-58) of :cite:`deFlorian:2016gvk` for q=u. @@ -168,7 +168,7 @@ def gamma_nspu(N, nf, sx, sx_ns_qed): Returns ------- gamma_nspu : complex - O(aem2) singlet-like non-singlet anomalous dimension + :math:`O(a_{em}^2)` singlet-like non-singlet anomalous dimension :math:`\\gamma_{ns,+,u}^{(0,2)}(N)` """ @@ -191,7 +191,7 @@ def gamma_nspu(N, nf, sx, sx_ns_qed): @nb.njit(cache=True) def gamma_nspd(N, nf, sx, sx_ns_qed): - r"""Compute the O(aem2) singlet-like non-singlet anomalous dimension for down quarks. + r"""Compute the :math:`O(a_{em}^2)` singlet-like non-singlet anomalous dimension for down quarks. Implements sum of Eqs. (57-58) of :cite:`deFlorian:2016gvk` for q=d. @@ -207,7 +207,7 @@ def gamma_nspd(N, nf, sx, sx_ns_qed): Returns ------- gamma_nspd : complex - O(aem2) singlet-like non-singlet anomalous dimension + :math:`O(a_{em}^2)` singlet-like non-singlet anomalous dimension :math:`\\gamma_{ns,+,d}^{(0,2)}(N)` """ @@ -230,7 +230,7 @@ def gamma_nspd(N, nf, sx, sx_ns_qed): @nb.njit(cache=True) def gamma_nsmu(N, nf, sx, sx_ns_qed): - r"""Compute the O(aem2) valence-like non-singlet anomalous dimension for up quarks. + r"""Compute the :math:`O(a_{em}^2)` valence-like non-singlet anomalous dimension for up quarks. Implements difference between Eqs. (57-58) of :cite:`deFlorian:2016gvk` for q=u. @@ -246,7 +246,7 @@ def gamma_nsmu(N, nf, sx, sx_ns_qed): Returns ------- gamma_nsp : complex - O(aem2) valence-like non-singlet anomalous dimension + :math:`O(a_{em}^2)` valence-like non-singlet anomalous dimension :math:`\\gamma_{ns,-,u}^{(0,2)}(N)` """ @@ -269,7 +269,7 @@ def gamma_nsmu(N, nf, sx, sx_ns_qed): @nb.njit(cache=True) def gamma_nsmd(N, nf, sx, sx_ns_qed): - r"""Compute the O(aem2) valence-like non-singlet anomalous dimension for down quarks. + r"""Compute the :math:`O(a_{em}^2)` valence-like non-singlet anomalous dimension for down quarks. Implements difference between Eqs. (57-58) of :cite:`deFlorian:2016gvk` for q=d. @@ -285,7 +285,7 @@ def gamma_nsmd(N, nf, sx, sx_ns_qed): Returns ------- gamma_nsp : complex - O(aem2) valence-like non-singlet anomalous dimension + :math:`O(a_{em}^2)` valence-like non-singlet anomalous dimension :math:`\\gamma_{ns,-,d}^{(0,2)}(N)` """ @@ -308,7 +308,7 @@ def gamma_nsmd(N, nf, sx, sx_ns_qed): @nb.njit(cache=True) def gamma_ps(N, nf): - r"""Compute the O(aem2) pure-singlet quark-quark anomalous dimension. + r"""Compute the :math:`O(a_{em}^2)` pure-singlet quark-quark anomalous dimension. Implements Eq. (59) of :cite:`deFlorian:2016gvk`. @@ -322,7 +322,7 @@ def gamma_ps(N, nf): Returns ------- gamma_ps : complex - O(aem2) pure-singlet quark-quark anomalous dimension + :math:`O(a_{em}^2)` pure-singlet quark-quark anomalous dimension :math:`\\gamma_{ps}^{(0,2)}(N)` """ @@ -337,7 +337,7 @@ def gamma_ps(N, nf): @nb.njit(cache=True) def gamma_singlet(N, nf, sx, sx_ns_qed): - r"""Compute the O(aem2) singlet sector. + r"""Compute the :math:`O(a_{em}^2)` singlet sector. Parameters ---------- @@ -351,7 +351,7 @@ def gamma_singlet(N, nf, sx, sx_ns_qed): Returns ------- gamma_singlet : numpy.ndarray - O(aem2) singlet anomalous dimension :math:`\\gamma_{S}^{(0,2)}(N,nf,sx)` + :math:`O(a_{em}^2)` singlet anomalous dimension :math:`\\gamma_{S}^{(0,2)}(N,nf,sx)` """ nu = constants.uplike_flavors(nf) nd = nf - nu @@ -409,7 +409,7 @@ def gamma_singlet(N, nf, sx, sx_ns_qed): @nb.njit(cache=True) def gamma_valence(N, nf, sx, sx_ns_qed): - r"""Compute the O(aem2) valence sector. + r"""Compute the :math:`O(a_{em}^2)` valence sector. Parameters ---------- @@ -423,7 +423,7 @@ def gamma_valence(N, nf, sx, sx_ns_qed): Returns ------- gamma_singlet : numpy.ndarray - O(aem2) valence anomalous dimension :math:`\\gamma_{V}^{(0,2)}(N,nf,sx)` + :math:`O(a_{em}^2)` valence anomalous dimension :math:`\\gamma_{V}^{(0,2)}(N,nf,sx)` """ nu = constants.uplike_flavors(nf) nd = nf - nu diff --git a/src/eko/anomalous_dimensions/as1aem1.py b/src/eko/anomalous_dimensions/as1aem1.py index 40bed34dd..7b8eaa442 100644 --- a/src/eko/anomalous_dimensions/as1aem1.py +++ b/src/eko/anomalous_dimensions/as1aem1.py @@ -1,4 +1,4 @@ -"""Contains the O(as1aem1) Altarelli-Parisi splitting kernels.""" +"""The :math:`O(a_s^1a_{em}^1)` Altarelli-Parisi splitting kernels.""" import numba as nb import numpy as np @@ -9,7 +9,7 @@ @nb.njit(cache=True) def gamma_phq(N, sx): - r"""Compute the O(as1aem1) photon-quark anomalous dimension. + r"""Compute the :math:`O(a_s^1a_{em}^1)` photon-quark anomalous dimension. Implements Eq. (36) of :cite:`deFlorian:2015ujt`. @@ -23,7 +23,7 @@ def gamma_phq(N, sx): Returns ------- gamma_phq : complex - O(as1aem1) photon-quark anomalous dimension :math:`\\gamma_{\\gamma q}^{(1,1)}(N)` + :math:`O(a_s^1a_{em}^1)` photon-quark anomalous dimension :math:`\\gamma_{\\gamma q}^{(1,1)}(N)` """ S1 = sx[0] @@ -54,7 +54,7 @@ def gamma_phq(N, sx): @nb.njit(cache=True) def gamma_qph(N, nf, sx): - r"""Compute the O(as1aem1) quark-photon anomalous dimension. + r"""Compute the :math:`O(a_s^1a_{em}^1)` quark-photon anomalous dimension. Implements Eq. (26) of :cite:`deFlorian:2015ujt`. @@ -70,7 +70,7 @@ def gamma_qph(N, nf, sx): Returns ------- gamma_qph : complex - O(as1aem1) quark-photon anomalous dimension :math:`\\gamma_{q \\gamma}^{(1,1)}(N)` + :math:`O(a_s^1a_{em}^1)` quark-photon anomalous dimension :math:`\\gamma_{q \\gamma}^{(1,1)}(N)` """ S1 = sx[0] @@ -102,7 +102,7 @@ def gamma_qph(N, nf, sx): @nb.njit(cache=True) def gamma_gph(N): - r"""Compute the O(as1aem1) gluon-photon anomalous dimension. + r"""Compute the :math:`O(a_s^1a_{em}^1)` gluon-photon anomalous dimension. Implements Eq. (27) of :cite:`deFlorian:2015ujt`. @@ -114,7 +114,7 @@ def gamma_gph(N): Returns ------- gamma_qph : complex - O(as1aem1) gluon-photon anomalous dimension :math:`\\gamma_{g \\gamma}^{(1,1)}(N)` + :math:`O(a_s^1a_{em}^1)` gluon-photon anomalous dimension :math:`\\gamma_{g \\gamma}^{(1,1)}(N)` """ return ( @@ -133,7 +133,7 @@ def gamma_gph(N): @nb.njit(cache=True) def gamma_phg(N): - r"""Compute the O(as1aem1) photon-gluon anomalous dimension. + r"""Compute the :math:`O(a_s^1a_{em}^1)` photon-gluon anomalous dimension. Implements Eq. (30) of :cite:`deFlorian:2015ujt`. @@ -145,7 +145,7 @@ def gamma_phg(N): Returns ------- gamma_qph : complex - O(as1aem1) photon-gluon anomalous dimension :math:`\\gamma_{\\gamma g}^{(1,1)}(N)` + :math:`O(a_s^1a_{em}^1)` photon-gluon anomalous dimension :math:`\\gamma_{\\gamma g}^{(1,1)}(N)` """ return constants.TR / constants.CF / constants.CA * constants.NC * gamma_gph(N) @@ -153,7 +153,7 @@ def gamma_phg(N): @nb.njit(cache=True) def gamma_qg(N, nf, sx): - r"""Compute the O(as1aem1) quark-gluon singlet anomalous dimension. + r"""Compute the :math:`O(a_s^1a_{em}^1)` quark-gluon singlet anomalous dimension. Implements Eq. (29) of :cite:`deFlorian:2015ujt`. @@ -169,7 +169,7 @@ def gamma_qg(N, nf, sx): Returns ------- gamma_qg : complex - O(as1aem1) quark-gluon singlet anomalous dimension + :math:`O(a_s^1a_{em}^1)` quark-gluon singlet anomalous dimension :math:`\\gamma_{qg}^{(1,1)}(N)` """ @@ -180,7 +180,7 @@ def gamma_qg(N, nf, sx): @nb.njit(cache=True) def gamma_gq(N, sx): - r"""Compute the O(as1aem1) gluon-quark singlet anomalous dimension. + r"""Compute the :math:`O(a_s^1a_{em}^1)` gluon-quark singlet anomalous dimension. Implements Eq. (35) of :cite:`deFlorian:2015ujt`. @@ -194,7 +194,7 @@ def gamma_gq(N, sx): Returns ------- gamma_gq : complex - O(as1aem1) gluon-quark singlet anomalous dimension + :math:`O(a_s^1a_{em}^1)` gluon-quark singlet anomalous dimension :math:`\\gamma_{gq}^{(1,1)}(N)` """ @@ -203,7 +203,7 @@ def gamma_gq(N, sx): @nb.njit(cache=True) def gamma_phph(nf): - r"""Compute the O(as1aem1) photon-photon singlet anomalous dimension. + r"""Compute the :math:`O(a_s^1a_{em}^1)` photon-photon singlet anomalous dimension. Implements Eq. (28) of :cite:`deFlorian:2015ujt`. @@ -215,7 +215,7 @@ def gamma_phph(nf): Returns ------- gamma_gg : complex - O(as1aem1) photon-photon singlet anomalous dimension + :math:`O(a_s^1a_{em}^1)` photon-photon singlet anomalous dimension :math:`\\gamma_{\\gamma \\gamma}^{(1,1)}(N)` """ @@ -226,14 +226,14 @@ def gamma_phph(nf): @nb.njit(cache=True) def gamma_gg(): - r"""Compute the O(as1aem1) gluon-gluon singlet anomalous dimension. + r"""Compute the :math:`O(a_s^1a_{em}^1)` gluon-gluon singlet anomalous dimension. Implements Eq. (31) of :cite:`deFlorian:2015ujt`. Returns ------- gamma_gg : complex - O(as1aem1) gluon-gluon singlet anomalous dimension + :math:`O(a_s^1a_{em}^1)` gluon-gluon singlet anomalous dimension :math:`\\gamma_{gg}^{(1,1)}(N)` """ @@ -242,7 +242,7 @@ def gamma_gg(): @nb.njit(cache=True) def gamma_nsp(N, sx, sx_ns_qed): - r"""Compute the O(as1aem1) singlet-like non-singlet anomalous dimension. + r"""Compute the :math:`O(a_s^1a_{em}^1)` singlet-like non-singlet anomalous dimension. Implements sum of Eqs. (33-34) of :cite:`deFlorian:2015ujt`. @@ -256,7 +256,7 @@ def gamma_nsp(N, sx, sx_ns_qed): Returns ------- gamma_nsp : complex - O(as1aem1) singlet-like non-singlet anomalous dimension + :math:`O(a_s^1a_{em}^1)` singlet-like non-singlet anomalous dimension :math:`\\gamma_{ns,+}^{(1)}(N)` """ @@ -309,7 +309,7 @@ def gamma_nsp(N, sx, sx_ns_qed): @nb.njit(cache=True) def gamma_nsm(N, sx, sx_ns_qed): - r"""Compute the O(as1aem1) valence-like non-singlet anomalous dimension. + r"""Compute the :math:`O(a_s^1a_{em}^1)` valence-like non-singlet anomalous dimension. Implements difference between Eqs. (33-34) of :cite:`deFlorian:2015ujt`. @@ -323,7 +323,7 @@ def gamma_nsm(N, sx, sx_ns_qed): Returns ------- gamma_nsm : complex - O(as1aem1) singlet-like non-singlet anomalous dimension + :math:`O(a_s^1a_{em}^1)` singlet-like non-singlet anomalous dimension :math:`\\gamma_{ns,-}^{(1,1)}(N)` """ @@ -372,7 +372,7 @@ def gamma_nsm(N, sx, sx_ns_qed): @nb.njit(cache=True) def gamma_singlet(N, nf, sx, sx_ns_qed): - r"""Compute the O(as1aem1) singlet sector. + r"""Compute the :math:`O(a_s^1a_{em}^1)` singlet sector. Parameters ---------- @@ -386,7 +386,7 @@ def gamma_singlet(N, nf, sx, sx_ns_qed): Returns ------- gamma_singlet : numpy.ndarray - O(as1aem1) singlet anomalous dimension :math:`\\gamma_{S}^{(1,1)}(N,nf,sx)` + :math:`O(a_s^1a_{em}^1)` singlet anomalous dimension :math:`\\gamma_{S}^{(1,1)}(N,nf,sx)` """ e2avg = constants.e2avg(nf) vue2m = constants.vue2m(nf) @@ -432,7 +432,7 @@ def gamma_singlet(N, nf, sx, sx_ns_qed): @nb.njit(cache=True) def gamma_valence(N, nf, sx, sx_ns_qed): - r"""Compute the O(as1aem1) valence sector. + r"""Compute the :math:`O(a_s^1a_{em}^1)` valence sector. Parameters ---------- @@ -446,7 +446,7 @@ def gamma_valence(N, nf, sx, sx_ns_qed): Returns ------- gamma_singlet : numpy.ndarray - O(as1aem1) valence anomalous dimension :math:`\\gamma_{V}^{(1,1)}(N,nf,sx)` + :math:`O(a_s^1a_{em}^1)` valence anomalous dimension :math:`\\gamma_{V}^{(1,1)}(N,nf,sx)` """ e2avg = constants.e2avg(nf) vue2m = constants.vue2m(nf) From 67aa5d3bddc0fad3fe03ecab535e2592f02c726c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 29 Nov 2022 11:13:00 +0100 Subject: [PATCH 246/312] Split evolution integrals for qed --- src/eko/kernels/evolution_integrals.py | 306 -------------------- src/eko/kernels/evolution_integrals_qed.py | 312 +++++++++++++++++++++ src/eko/kernels/non_singlet_qed.py | 42 +-- tests/eko/test_kernels_ei.py | 19 +- 4 files changed, 343 insertions(+), 336 deletions(-) create mode 100644 src/eko/kernels/evolution_integrals_qed.py diff --git a/src/eko/kernels/evolution_integrals.py b/src/eko/kernels/evolution_integrals.py index 08d4d7cec..667adeb14 100644 --- a/src/eko/kernels/evolution_integrals.py +++ b/src/eko/kernels/evolution_integrals.py @@ -41,64 +41,6 @@ def j00(a1, a0, nf): return np.log(a1 / a0) / beta.beta_qcd((2, 0), nf) -@nb.njit(cache=True) -def j00_qed(a1, a0, aem, nf): - r""" - LO-LO QED exact evolution integral. - - .. math:: - j^{(0,0)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{(\beta_0 + aem \beta_{0,1}) a_s'} - = \frac{\ln(a_s/a_s^0)}{\beta_0 + aem \beta_{0,1}} - - Parameters - ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors - - Returns - ------- - j00 : float - integral - """ - beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) - return np.log(a1 / a0) / beta0 - - -@nb.njit(cache=True) -def jm10(a1, a0, aem, nf): - r""" - LO-LO QED exact evolution integral. - - .. math:: - j^{(-1,0)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{(\beta_0 + aem \beta_{0,1}) a_s'^2} - = \frac{1.0 / a0 - 1.0 / as}{\beta_0 + aem \beta_{0,1}} - - Parameters - ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors - - Returns - ------- - j00 : float - integral - """ - beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) - return (1.0 / a0 - 1.0 / a1) / beta0 - - @nb.njit(cache=True) def j11_exact(a1, a0, nf): r""" @@ -128,38 +70,6 @@ def j11_exact(a1, a0, nf): return (1.0 / beta_qcd_as3) * np.log((1.0 + a1 * b1) / (1.0 + a0 * b1)) -@nb.njit(cache=True) -def j11_exact_qed(a1, a0, aem, nf): - r""" - NLO-NLO exact evolution integral. - - .. math:: - j^{(1,1)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s}\!da_s'\, - \frac{a_s'^2}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3} - = \frac{1}{\beta_1}\ln\left(\frac{1+b_1 a_s}{1+b_1 a_s^0}\right) - - Parameters - ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors - - Returns - ------- - j11 : float - integral - """ - beta1 = beta.beta_qcd((3, 0), nf) - beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) - b1 = beta1 / beta0 - return (1.0 / beta1) * np.log((1.0 + a1 * b1) / (1.0 + a0 * b1)) - - @nb.njit(cache=True) def j11_expanded(a1, a0, nf): r""" @@ -212,38 +122,6 @@ def j01_exact(a1, a0, nf): return j00(a1, a0, nf) - beta.b_qcd((3, 0), nf) * j11_exact(a1, a0, nf) -@nb.njit(cache=True) -def j01_exact_qed(a1, a0, aem, nf): - r""" - LO-NLO QED exact evolution integral. - - .. math:: - j^{(0,1)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s}\!da_s'\, - \frac{a_s'}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3} - = j^{(0,0)}(a_s,a_s^0,aem) - b_1 j^{(1,1)}(a_s,a_s^0,aem) - - Parameters - ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors - - Returns - ------- - j11 : float - integral - """ - beta1 = beta.beta_qcd((3, 0), nf) - beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) - b1 = beta1 / beta0 - return j00_qed(a1, a0, aem, nf) - b1 * j11_exact_qed(a1, a0, aem, nf) - - @nb.njit(cache=True) def j01_expanded(a1, a0, nf): r""" @@ -269,36 +147,6 @@ def j01_expanded(a1, a0, nf): return j00(a1, a0, nf) - beta.b_qcd((3, 0), nf) * j11_expanded(a1, a0, nf) -@nb.njit(cache=True) -def jm11_exact(a1, a0, aem, nf): - r""" - LO-NLO exact evolution integral. - - .. math:: - j^{(-1,1)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3} - = \frac{1.0 / a0 - 1.0 / as}{\beta_0 + aem \beta_{0,1}} + \frac{b_1}{(\beta_0 + aem \beta_{0,1}} \left(\log(1 + 1 / (as b_1)) - \log(1 + 1 / (a0 b_1)\right) - - Parameters - ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - nf : int - number of active flavors - - Returns - ------- - j11 : float - integral - """ - beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) - b1 = beta.beta_qcd((3, 0), nf) / beta0 - return -(1.0 / a1 - 1.0 / a0) / beta0 + b1 / beta0 * ( - np.log(1.0 + 1.0 / (a1 * b1)) - np.log(1.0 + 1.0 / (a0 * b1)) - ) - - @nb.njit(cache=True) def j22_exact(a1, a0, nf): r""" @@ -342,52 +190,6 @@ def j22_exact(a1, a0, nf): ) * np.real(delta / Delta) -@nb.njit(cache=True) -def j22_exact_qed(a1, a0, aem, nf): - r""" - NNLO-NNLO exact evolution integral. - - .. math:: - j^{(2,2)}(a_s,a_s^0,aem) &= - \int\limits_{a_s^0}^{a_s}\!da_s'\,\frac{a_s'^3} - {(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4} - = \frac{1}{\beta_2}\ln\left( - \frac{1 + a_s ( b_1 + b_2 a_s ) }{ 1 + a_s^0 ( b_1 + b_2 a_s^0 )}\right) - - \frac{b_1 \delta}{ \beta_2 \Delta} \\ - \delta &= \atan \left( \frac{b_1 + 2 a_s b_2 }{ \Delta} \right) - - \atan \left( \frac{b_1 + 2 a_s^0 b_2 }{ \Delta} \right) \\ - \Delta &= \sqrt{4 b_2 - b_1^2} - - Parameters - ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors - - Returns - ------- - j22 : complex - integral - """ - beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) - b1 = beta.beta_qcd((3, 0), nf) / beta0 - b2 = beta.beta_qcd((4, 0), nf) / beta0 - # allow Delta to be complex for nf = 6, the final result will be real - Delta = np.sqrt(complex(4 * b2 - b1**2)) - delta = np.arctan((b1 + 2 * a1 * b2) / Delta) - np.arctan( - (b1 + 2 * a0 * b2) / Delta - ) - log = np.log((1 + a1 * (b1 + b2 * a1)) / (1 + a0 * (b1 + b2 * a0))) - return 1 / (2 * beta.beta_qcd((4, 0), nf)) * log - b1 / ( - beta.beta_qcd((4, 0), nf) - ) * np.real(delta / Delta) - - @nb.njit(cache=True) def j12_exact(a1, a0, nf): r""" @@ -423,44 +225,6 @@ def j12_exact(a1, a0, nf): return 2.0 / (beta.beta_qcd((2, 0), nf)) * np.real(delta / Delta) -@nb.njit(cache=True) -def j12_exact_qed(a1, a0, aem, nf): - r""" - NLO-NNLO exact evolution integral. - - .. math:: - j^{(1,2)}(a_s,a_s^0,aem) &= \int\limits_{a_s^0}^{a_s}\!da_s'\,\frac{a_s'^2}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ - &= \frac{2 \delta}{\beta_0 \Delta} \\ - \delta &= \atan \left( \frac{b_1 + 2 a_s b_2 }{ \Delta} \right) - \atan \left( \frac{b_1 + 2 a_s^0 b_2 }{ \Delta} \right) \\ - \Delta &= \sqrt{4 b_2 - b_1^2} - - Parameters - ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors - - Returns - ------- - j12 : complex - integral - """ # pylint: disable=line-too-long - beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) - b1 = beta.beta_qcd((3, 0), nf) / beta0 - b2 = beta.beta_qcd((4, 0), nf) / beta0 - # allow Delta to be complex for nf = 6, the final result will be real - Delta = np.sqrt(complex(4 * b2 - b1**2)) - delta = np.arctan((b1 + 2 * a1 * b2) / Delta) - np.arctan( - (b1 + 2 * a0 * b2) / Delta - ) - return 2.0 / (beta0) * np.real(delta / Delta) - - @nb.njit(cache=True) def j02_exact(a1, a0, nf): r""" @@ -492,76 +256,6 @@ def j02_exact(a1, a0, nf): ) -@nb.njit(cache=True) -def j02_exact_qed(a1, a0, aem, nf): - r""" - LO-NNLO exact evolution integral. - - .. math:: - j^{(0,2)}(a_s,a_s^0,aem) &= \int\limits_{a_s^0}^{a_s}\!da_s'\, - \frac{a_s'}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ - &= j^{(0,0)}(a_s,a_s^0) - b_1 j^{(1,2)}(a_s,a_s^0) - b_2 j^{(2,2)}(a_s,a_s^0) - - Parameters - ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors - - Returns - ------- - j02 : complex - integral - """ - beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) - b1 = beta.beta_qcd((3, 0), nf) / beta0 - b2 = beta.beta_qcd((4, 0), nf) / beta0 - return ( - j00_qed(a1, a0, aem, nf) - - b1 * j12_exact_qed(a1, a0, aem, nf) - - b2 * j22_exact_qed(a1, a0, aem, nf) - ) - - -@nb.njit(cache=True) -def jm12_exact(a1, a0, aem, nf): - r""" - LO-NNLO exact evolution integral. - - .. math:: - j^{(-1,2)}(a_s,a_s^0,aem) &= \int\limits_{a_s^0}^{a_s}\!da_s'\, - \frac{1}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ - &= j^{(-1,0)}(a_s,a_s^0,aem) - b_1 j^{(0,2)}(a_s,a_s^0) - b_2 j^{(1,2)}(a_s,a_s^0) - - Parameters - ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - nf : int - number of active flavors - - Returns - ------- - j02 : complex - integral - """ - beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) - b1 = beta.beta_qcd((3, 0), nf) / beta0 - b2 = beta.beta_qcd((4, 0), nf) / beta0 - return ( - jm10(a1, a0, aem, nf) - - b1 * j02_exact_qed(a1, a0, aem, nf) - - b2 * j12_exact_qed(a1, a0, aem, nf) - ) - - @nb.njit(cache=True) def j22_expanded(a1, a0, nf): r""" diff --git a/src/eko/kernels/evolution_integrals_qed.py b/src/eko/kernels/evolution_integrals_qed.py new file mode 100644 index 000000000..077054e1a --- /dev/null +++ b/src/eko/kernels/evolution_integrals_qed.py @@ -0,0 +1,312 @@ +r"""Compute evolution integrals needed for QED.""" +import numba as nb +import numpy as np + +from .. import beta +from . import evolution_integrals as ei + + +@nb.njit(cache=True) +def j00_qed(a1, a0, aem, nf): + r""" + LO-LO QED exact evolution integral. + + .. math:: + j^{(0,0)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{(\beta_0 + aem \beta_{0,1}) a_s'} + = \frac{\ln(a_s/a_s^0)}{\beta_0 + aem \beta_{0,1}} + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + aem : float + electromagnetic coupling value + nf : int + number of active flavors + + Returns + ------- + j00 : float + integral + """ + beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) + return np.log(a1 / a0) / beta0 + + +@nb.njit(cache=True) +def jm10(a1, a0, aem, nf): + r""" + LO-LO QED exact evolution integral. + + .. math:: + j^{(-1,0)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{(\beta_0 + aem \beta_{0,1}) a_s'^2} + = \frac{1.0 / a0 - 1.0 / as}{\beta_0 + aem \beta_{0,1}} + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + aem : float + electromagnetic coupling value + nf : int + number of active flavors + + Returns + ------- + j00 : float + integral + """ + beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) + return (1.0 / a0 - 1.0 / a1) / beta0 + + +@nb.njit(cache=True) +def j11_exact_qed(a1, a0, aem, nf): + r""" + NLO-NLO exact evolution integral. + + .. math:: + j^{(1,1)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s}\!da_s'\, + \frac{a_s'^2}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3} + = \frac{1}{\beta_1}\ln\left(\frac{1+b_1 a_s}{1+b_1 a_s^0}\right) + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + aem : float + electromagnetic coupling value + nf : int + number of active flavors + + Returns + ------- + j11 : float + integral + """ + beta1 = beta.beta_qcd((3, 0), nf) + beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) + b1 = beta1 / beta0 + return (1.0 / beta1) * np.log((1.0 + a1 * b1) / (1.0 + a0 * b1)) + + +@nb.njit(cache=True) +def j01_exact_qed(a1, a0, aem, nf): + r""" + LO-NLO QED exact evolution integral. + + .. math:: + j^{(0,1)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s}\!da_s'\, + \frac{a_s'}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3} + = j^{(0,0)}(a_s,a_s^0,aem) - b_1 j^{(1,1)}(a_s,a_s^0,aem) + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + aem : float + electromagnetic coupling value + nf : int + number of active flavors + + Returns + ------- + j11 : float + integral + """ + beta1 = beta.beta_qcd((3, 0), nf) + beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) + b1 = beta1 / beta0 + return j00_qed(a1, a0, aem, nf) - b1 * j11_exact_qed(a1, a0, aem, nf) + + +@nb.njit(cache=True) +def jm11_exact(a1, a0, aem, nf): + r""" + LO-NLO exact evolution integral. + + .. math:: + j^{(-1,1)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3} + = \frac{1.0 / a0 - 1.0 / as}{\beta_0 + aem \beta_{0,1}} + \frac{b_1}{(\beta_0 + aem \beta_{0,1}} \left(\log(1 + 1 / (as b_1)) - \log(1 + 1 / (a0 b_1)\right) + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + + Returns + ------- + j11 : float + integral + """ + beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) + b1 = beta.beta_qcd((3, 0), nf) / beta0 + return -(1.0 / a1 - 1.0 / a0) / beta0 + b1 / beta0 * ( + np.log(1.0 + 1.0 / (a1 * b1)) - np.log(1.0 + 1.0 / (a0 * b1)) + ) + + +@nb.njit(cache=True) +def j22_exact_qed(a1, a0, aem, nf): + r""" + NNLO-NNLO exact evolution integral. + + .. math:: + j^{(2,2)}(a_s,a_s^0,aem) &= + \int\limits_{a_s^0}^{a_s}\!da_s'\,\frac{a_s'^3} + {(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4} + = \frac{1}{\beta_2}\ln\left( + \frac{1 + a_s ( b_1 + b_2 a_s ) }{ 1 + a_s^0 ( b_1 + b_2 a_s^0 )}\right) + - \frac{b_1 \delta}{ \beta_2 \Delta} \\ + \delta &= \atan \left( \frac{b_1 + 2 a_s b_2 }{ \Delta} \right) + - \atan \left( \frac{b_1 + 2 a_s^0 b_2 }{ \Delta} \right) \\ + \Delta &= \sqrt{4 b_2 - b_1^2} + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + aem : float + electromagnetic coupling value + nf : int + number of active flavors + + Returns + ------- + j22 : complex + integral + """ + beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) + b1 = beta.beta_qcd((3, 0), nf) / beta0 + b2 = beta.beta_qcd((4, 0), nf) / beta0 + # allow Delta to be complex for nf = 6, the final result will be real + Delta = np.sqrt(complex(4 * b2 - b1**2)) + delta = np.arctan((b1 + 2 * a1 * b2) / Delta) - np.arctan( + (b1 + 2 * a0 * b2) / Delta + ) + log = np.log((1 + a1 * (b1 + b2 * a1)) / (1 + a0 * (b1 + b2 * a0))) + return 1 / (2 * beta.beta_qcd((4, 0), nf)) * log - b1 / ( + beta.beta_qcd((4, 0), nf) + ) * np.real(delta / Delta) + + +@nb.njit(cache=True) +def j12_exact_qed(a1, a0, aem, nf): + r""" + NLO-NNLO exact evolution integral. + + .. math:: + j^{(1,2)}(a_s,a_s^0,aem) &= \int\limits_{a_s^0}^{a_s}\!da_s'\,\frac{a_s'^2}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ + &= \frac{2 \delta}{\beta_0 \Delta} \\ + \delta &= \atan \left( \frac{b_1 + 2 a_s b_2 }{ \Delta} \right) - \atan \left( \frac{b_1 + 2 a_s^0 b_2 }{ \Delta} \right) \\ + \Delta &= \sqrt{4 b_2 - b_1^2} + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + aem : float + electromagnetic coupling value + nf : int + number of active flavors + + Returns + ------- + j12 : complex + integral + """ # pylint: disable=line-too-long + beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) + b1 = beta.beta_qcd((3, 0), nf) / beta0 + b2 = beta.beta_qcd((4, 0), nf) / beta0 + # allow Delta to be complex for nf = 6, the final result will be real + Delta = np.sqrt(complex(4 * b2 - b1**2)) + delta = np.arctan((b1 + 2 * a1 * b2) / Delta) - np.arctan( + (b1 + 2 * a0 * b2) / Delta + ) + return 2.0 / (beta0) * np.real(delta / Delta) + + +@nb.njit(cache=True) +def j02_exact_qed(a1, a0, aem, nf): + r""" + LO-NNLO exact evolution integral. + + .. math:: + j^{(0,2)}(a_s,a_s^0,aem) &= \int\limits_{a_s^0}^{a_s}\!da_s'\, + \frac{a_s'}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ + &= j^{(0,0)}(a_s,a_s^0) - b_1 j^{(1,2)}(a_s,a_s^0) - b_2 j^{(2,2)}(a_s,a_s^0) + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + aem : float + electromagnetic coupling value + nf : int + number of active flavors + + Returns + ------- + j02 : complex + integral + """ + beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) + b1 = beta.beta_qcd((3, 0), nf) / beta0 + b2 = beta.beta_qcd((4, 0), nf) / beta0 + return ( + j00_qed(a1, a0, aem, nf) + - b1 * j12_exact_qed(a1, a0, aem, nf) + - b2 * j22_exact_qed(a1, a0, aem, nf) + ) + + +@nb.njit(cache=True) +def jm12_exact(a1, a0, aem, nf): + r""" + LO-NNLO exact evolution integral. + + .. math:: + j^{(-1,2)}(a_s,a_s^0,aem) &= \int\limits_{a_s^0}^{a_s}\!da_s'\, + \frac{1}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ + &= j^{(-1,0)}(a_s,a_s^0,aem) - b_1 j^{(0,2)}(a_s,a_s^0) - b_2 j^{(1,2)}(a_s,a_s^0) + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + + Returns + ------- + j02 : complex + integral + """ + beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) + b1 = beta.beta_qcd((3, 0), nf) / beta0 + b2 = beta.beta_qcd((4, 0), nf) / beta0 + return ( + jm10(a1, a0, aem, nf) + - b1 * j02_exact_qed(a1, a0, aem, nf) + - b2 * j12_exact_qed(a1, a0, aem, nf) + ) diff --git a/src/eko/kernels/non_singlet_qed.py b/src/eko/kernels/non_singlet_qed.py index 478f38514..28ea4d6e0 100644 --- a/src/eko/kernels/non_singlet_qed.py +++ b/src/eko/kernels/non_singlet_qed.py @@ -2,9 +2,8 @@ import numba as nb import numpy as np -from .. import beta -from . import evolution_integrals as ei -from . import non_singlet, utils +from . import evolution_integrals_qed as ei_qed +from . import utils @nb.njit(cache=True) @@ -31,8 +30,8 @@ def as1aem1_exact(gamma_ns, a1, a0, aem, nf): O(as1aem1) non-singlet exact EKO """ return np.exp( - (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j00_qed(a1, a0, aem, nf) - + aem * gamma_ns[0, 1] * ei.jm10(a1, a0, aem, nf) + (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei_qed.j00_qed(a1, a0, aem, nf) + + aem * gamma_ns[0, 1] * ei_qed.jm10(a1, a0, aem, nf) ) @@ -60,8 +59,9 @@ def as1aem2_exact(gamma_ns, a1, a0, aem, nf): O(as1aem2) non-singlet exact EKO """ return np.exp( - (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j00_qed(a1, a0, aem, nf) - + (aem * gamma_ns[0, 1] + aem**2 * gamma_ns[0, 2]) * ei.jm10(a1, a0, aem, nf) + (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei_qed.j00_qed(a1, a0, aem, nf) + + (aem * gamma_ns[0, 1] + aem**2 * gamma_ns[0, 2]) + * ei_qed.jm10(a1, a0, aem, nf) ) @@ -89,9 +89,9 @@ def as2aem1_exact(gamma_ns, a1, a0, aem, nf): O(as2aem1) non-singlet exact EKO """ return np.exp( - (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j01_exact_qed(a1, a0, aem, nf) - + gamma_ns[2, 0] * ei.j11_exact_qed(a1, a0, aem, nf) - + aem * gamma_ns[0, 1] * ei.jm11_exact(a1, a0, aem, nf) + (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei_qed.j01_exact_qed(a1, a0, aem, nf) + + gamma_ns[2, 0] * ei_qed.j11_exact_qed(a1, a0, aem, nf) + + aem * gamma_ns[0, 1] * ei_qed.jm11_exact(a1, a0, aem, nf) ) @@ -119,10 +119,10 @@ def as2aem2_exact(gamma_ns, a1, a0, aem, nf): O(as2aem2) non-singlet exact EKO """ return np.exp( - (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j01_exact_qed(a1, a0, aem, nf) - + gamma_ns[2, 0] * ei.j11_exact_qed(a1, a0, aem, nf) + (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei_qed.j01_exact_qed(a1, a0, aem, nf) + + gamma_ns[2, 0] * ei_qed.j11_exact_qed(a1, a0, aem, nf) + (aem * gamma_ns[0, 1] + aem**2 * gamma_ns[0, 2]) - * ei.jm11_exact(a1, a0, aem, nf) + * ei_qed.jm11_exact(a1, a0, aem, nf) ) @@ -150,10 +150,10 @@ def as3aem1_exact(gamma_ns, a1, a0, aem, nf): O(as3aem1) non-singlet exact EKO """ return np.exp( - (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j02_exact_qed(a1, a0, aem, nf) - + gamma_ns[2, 0] * ei.j12_exact_qed(a1, a0, aem, nf) - + gamma_ns[3, 0] * ei.j22_exact_qed(a1, a0, aem, nf) - + aem * gamma_ns[0, 1] * ei.jm12_exact(a1, a0, aem, nf) + (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei_qed.j02_exact_qed(a1, a0, aem, nf) + + gamma_ns[2, 0] * ei_qed.j12_exact_qed(a1, a0, aem, nf) + + gamma_ns[3, 0] * ei_qed.j22_exact_qed(a1, a0, aem, nf) + + aem * gamma_ns[0, 1] * ei_qed.jm12_exact(a1, a0, aem, nf) ) @@ -181,11 +181,11 @@ def as3aem2_exact(gamma_ns, a1, a0, aem, nf): O(as3aem2) non-singlet exact EKO """ return np.exp( - (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j02_exact_qed(a1, a0, aem, nf) - + gamma_ns[2, 0] * ei.j12_exact_qed(a1, a0, aem, nf) - + gamma_ns[3, 0] * ei.j22_exact_qed(a1, a0, aem, nf) + (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei_qed.j02_exact_qed(a1, a0, aem, nf) + + gamma_ns[2, 0] * ei_qed.j12_exact_qed(a1, a0, aem, nf) + + gamma_ns[3, 0] * ei_qed.j22_exact_qed(a1, a0, aem, nf) + (aem * gamma_ns[0, 1] + aem**2 * gamma_ns[0, 2]) - * ei.jm12_exact(a1, a0, aem, nf) + * ei_qed.jm12_exact(a1, a0, aem, nf) ) diff --git a/tests/eko/test_kernels_ei.py b/tests/eko/test_kernels_ei.py index 9971da121..0058079a2 100644 --- a/tests/eko/test_kernels_ei.py +++ b/tests/eko/test_kernels_ei.py @@ -2,6 +2,7 @@ from eko import beta from eko.kernels import evolution_integrals as ei +from eko.kernels import evolution_integrals_qed as ei_qed def test_zero(): @@ -27,15 +28,15 @@ def test_zero_qed(): """No evolution results in exp(0)""" nf = 3 for fnc in [ - ei.j00_qed, - ei.jm10, - ei.j11_exact_qed, - ei.j01_exact_qed, - ei.jm11_exact, - ei.j22_exact_qed, - ei.j12_exact_qed, - ei.j02_exact_qed, - ei.jm12_exact, + ei_qed.j00_qed, + ei_qed.jm10, + ei_qed.j11_exact_qed, + ei_qed.j01_exact_qed, + ei_qed.jm11_exact, + ei_qed.j22_exact_qed, + ei_qed.j12_exact_qed, + ei_qed.j02_exact_qed, + ei_qed.jm12_exact, ]: np.testing.assert_allclose(fnc(1, 1, 0.00058, nf), 0) From 7bd5a63ba068993a1d4dcc6deae0c7d72893ddbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 29 Nov 2022 11:30:55 +0100 Subject: [PATCH 247/312] Rename gamma_QEDsinglet -> gamma_singlet_qed --- src/eko/anomalous_dimensions/__init__.py | 24 ++++++++++++------------ src/eko/anomalous_dimensions/as1.py | 4 ++-- src/eko/anomalous_dimensions/as2.py | 4 ++-- src/eko/anomalous_dimensions/as3.py | 4 ++-- tests/eko/test_ad.py | 16 ++++++++-------- 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index 309b02b0c..cd39b6652 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -413,9 +413,9 @@ def gamma_singlet_qed(order, n, nf): See Also -------- - eko.anomalous_dimensions.as1.gamma_QEDsinglet : :math:`\gamma_{S}^{(0)}(N)` - eko.anomalous_dimensions.as2.gamma_QEDsinglet : :math:`\gamma_{S}^{(1)}(N)` - eko.anomalous_dimensions.as3.gamma_QEDsinglet : :math:`\gamma_{S}^{(2)}(N)` + eko.anomalous_dimensions.as1.gamma_singlet_qed : :math:`\gamma_{S}^{(0)}(N)` + eko.anomalous_dimensions.as2.gamma_singlet_qed : :math:`\gamma_{S}^{(1)}(N)` + eko.anomalous_dimensions.as3.gamma_singlet_qed : :math:`\gamma_{S}^{(2)}(N)` eko.anomalous_dimensions.aem1.gamma_singlet : :math:`\gamma_{S}^{(0,1)}(N)` eko.anomalous_dimensions.as1aem1.gamma_singlet : :math:`\gamma_{S}^{(1,1)}(N)` eko.anomalous_dimensions.aem2.gamma_singlet : :math:`\gamma_{S}^{(0,2)}(N)` @@ -430,17 +430,17 @@ def gamma_singlet_qed(order, n, nf): sx_ns_qed = harmonics.compute_qed_ns_cache(n, sx[0]) gamma_s = np.zeros((order[0] + 1, order[1] + 1, 4, 4), np.complex_) if order[0] >= 1: - gamma_s[1, 0] = as1.gamma_QEDsinglet(n, sx[0], nf) + gamma_s[1, 0] = as1.gamma_singlet_qed(n, sx[0], nf) if order[1] >= 1: gamma_s[0, 1] = aem1.gamma_singlet(n, nf, sx) if order[0] >= 1 and order[1] >= 1: gamma_s[1, 1] = as1aem1.gamma_singlet(n, nf, sx, sx_ns_qed) if order[0] >= 2: - gamma_s[2, 0] = as2.gamma_QEDsinglet(n, nf, sx) + gamma_s[2, 0] = as2.gamma_singlet_qed(n, nf, sx) if order[1] >= 2: gamma_s[0, 2] = aem2.gamma_singlet(n, nf, sx, sx_ns_qed) if order[0] >= 3: - gamma_s[3, 0] = as3.gamma_QEDsinglet(n, nf, sx) + gamma_s[3, 0] = as3.gamma_singlet_qed(n, nf, sx) return gamma_s @@ -465,9 +465,9 @@ def gamma_valence_qed(order, n, nf): See Also -------- - eko.anomalous_dimensions.as1.gamma_QEDvalence : :math:`\gamma_{V}^{(0)}(N)` - eko.anomalous_dimensions.as2.gamma_QEDvalence : :math:`\gamma_{V}^{(1)}(N)` - eko.anomalous_dimensions.as3.gamma_QEDvalence : :math:`\gamma_{V}^{(2)}(N)` + eko.anomalous_dimensions.as1.gamma_valence_qed : :math:`\gamma_{V}^{(0)}(N)` + eko.anomalous_dimensions.as2.gamma_valence_qed : :math:`\gamma_{V}^{(1)}(N)` + eko.anomalous_dimensions.as3.gamma_valence_qed : :math:`\gamma_{V}^{(2)}(N)` eko.anomalous_dimensions.aem1.gamma_valence : :math:`\gamma_{V}^{(0,1)}(N)` eko.anomalous_dimensions.as1aem1.gamma_valence : :math:`\gamma_{V}^{(1,1)}(N)` eko.anomalous_dimensions.aem2.gamma_valence : :math:`\gamma_{V}^{(0,2)}(N)` @@ -482,15 +482,15 @@ def gamma_valence_qed(order, n, nf): sx_ns_qed = harmonics.compute_qed_ns_cache(n, sx[0]) gamma_v = np.zeros((order[0] + 1, order[1] + 1, 2, 2), np.complex_) if order[0] >= 1: - gamma_v[1, 0] = as1.gamma_QEDvalence(n, sx[0]) + gamma_v[1, 0] = as1.gamma_valence_qed(n, sx[0]) if order[1] >= 1: gamma_v[0, 1] = aem1.gamma_valence(n, nf, sx) if order[0] >= 1 and order[1] >= 1: gamma_v[1, 1] = as1aem1.gamma_valence(n, nf, sx, sx_ns_qed) if order[0] >= 2: - gamma_v[2, 0] = as2.gamma_QEDvalence(n, nf, sx) + gamma_v[2, 0] = as2.gamma_valence_qed(n, nf, sx) if order[1] >= 2: gamma_v[0, 2] = aem2.gamma_valence(n, nf, sx, sx_ns_qed) if order[0] >= 3: - gamma_v[3, 0] = as3.gamma_QEDvalence(n, nf, sx) + gamma_v[3, 0] = as3.gamma_valence_qed(n, nf, sx) return gamma_v diff --git a/src/eko/anomalous_dimensions/as1.py b/src/eko/anomalous_dimensions/as1.py index bff8ec1ab..181943d32 100644 --- a/src/eko/anomalous_dimensions/as1.py +++ b/src/eko/anomalous_dimensions/as1.py @@ -142,7 +142,7 @@ def gamma_singlet(N, s1, nf): @nb.njit(cache=True) -def gamma_QEDsinglet(N, s1, nf): +def gamma_singlet_qed(N, s1, nf): r""" Compute the leading-order singlet anomalous dimension matrix. @@ -187,7 +187,7 @@ def gamma_QEDsinglet(N, s1, nf): @nb.njit(cache=True) -def gamma_QEDvalence(N, s1): +def gamma_valence_qed(N, s1): r""" Compute the leading-order valence anomalous dimension matrix. diff --git a/src/eko/anomalous_dimensions/as2.py b/src/eko/anomalous_dimensions/as2.py index a15441253..8ba8f8666 100644 --- a/src/eko/anomalous_dimensions/as2.py +++ b/src/eko/anomalous_dimensions/as2.py @@ -276,7 +276,7 @@ def gamma_singlet(n, nf, sx): @nb.njit(cache=True) -def gamma_QEDsinglet(N, nf, sx): +def gamma_singlet_qed(N, nf, sx): r""" Compute the leading-order singlet anomalous dimension matrix. @@ -322,7 +322,7 @@ def gamma_QEDsinglet(N, nf, sx): @nb.njit(cache=True) -def gamma_QEDvalence(N, nf, sx): +def gamma_valence_qed(N, nf, sx): r""" Compute the leading-order valence anomalous dimension matrix. diff --git a/src/eko/anomalous_dimensions/as3.py b/src/eko/anomalous_dimensions/as3.py index 9e609ad67..93e4ad066 100644 --- a/src/eko/anomalous_dimensions/as3.py +++ b/src/eko/anomalous_dimensions/as3.py @@ -588,7 +588,7 @@ def gamma_singlet(N, nf, sx): @nb.njit(cache=True) -def gamma_QEDsinglet(N, nf, sx): +def gamma_singlet_qed(N, nf, sx): r""" Compute the leading-order singlet anomalous dimension matrix. @@ -634,7 +634,7 @@ def gamma_QEDsinglet(N, nf, sx): @nb.njit(cache=True) -def gamma_QEDvalence(N, nf, sx): +def gamma_valence_qed(N, nf, sx): r""" Compute the leading-order valence anomalous dimension matrix. diff --git a/tests/eko/test_ad.py b/tests/eko/test_ad.py index affe0c343..4993bfc8f 100644 --- a/tests/eko/test_ad.py +++ b/tests/eko/test_ad.py @@ -42,11 +42,11 @@ def test_exp_matrix(): res = ad.exp_matrix_2D(gamma_S_0)[0] res2 = ad.exp_matrix(gamma_S_0)[0] assert_allclose(res, res2) - gamma_S_0_qed = as1.gamma_QEDsinglet(3, s1, NF) + gamma_S_0_qed = as1.gamma_singlet_qed(3, s1, NF) res = expm(gamma_S_0_qed) res2 = ad.exp_matrix(gamma_S_0_qed)[0] assert_allclose(res, res2) - gamma_v_0_qed = as1.gamma_QEDvalence(3, s1) + gamma_v_0_qed = as1.gamma_valence_qed(3, s1) res = expm(gamma_v_0_qed) res2 = ad.exp_matrix(gamma_v_0_qed)[0] assert_allclose(res, res2) @@ -193,11 +193,11 @@ def test_dim_singlet(): sx = harmonics.sx(N, max_weight=3 + 1) gamma_singlet = ad.gamma_singlet_qed((3, 2), N, nf) assert gamma_singlet.shape == (4, 3, 4, 4) - gamma_singlet_as1 = as1.gamma_QEDsinglet(N, sx[0], nf) + gamma_singlet_as1 = as1.gamma_singlet_qed(N, sx[0], nf) assert gamma_singlet_as1.shape == (4, 4) - gamma_singlet_as2 = as2.gamma_QEDsinglet(N, nf, sx) + gamma_singlet_as2 = as2.gamma_singlet_qed(N, nf, sx) assert gamma_singlet_as2.shape == (4, 4) - gamma_singlet_as3 = as3.gamma_QEDsinglet(N, nf, sx) + gamma_singlet_as3 = as3.gamma_singlet_qed(N, nf, sx) assert gamma_singlet_as3.shape == (4, 4) @@ -207,11 +207,11 @@ def test_dim_valence(): sx = harmonics.sx(N, max_weight=3 + 1) gamma_valence = ad.gamma_valence_qed((3, 2), N, nf) assert gamma_valence.shape == (4, 3, 2, 2) - gamma_valence_as1 = as1.gamma_QEDvalence(N, sx[0]) + gamma_valence_as1 = as1.gamma_valence_qed(N, sx[0]) assert gamma_valence_as1.shape == (2, 2) - gamma_valence_as2 = as2.gamma_QEDvalence(N, nf, sx) + gamma_valence_as2 = as2.gamma_valence_qed(N, nf, sx) assert gamma_valence_as2.shape == (2, 2) - gamma_valence_as3 = as3.gamma_QEDvalence(N, nf, sx) + gamma_valence_as3 = as3.gamma_valence_qed(N, nf, sx) assert gamma_valence_as3.shape == (2, 2) From d8e2dca8e1050ec6a6982b0cdd0a73ef502113db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 29 Nov 2022 11:49:10 +0100 Subject: [PATCH 248/312] Refactor calls to quad_ker --- src/eko/evolution_operator/__init__.py | 388 +++++++++++++++++-------- 1 file changed, 270 insertions(+), 118 deletions(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 99b78d4a0..8726b90d6 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -254,133 +254,285 @@ def quad_ker( integrand = ker_base.integrand(areas) if integrand == 0.0: return 0.0 - # TODO refactor calls if order[1] == 0: - # compute the actual evolution kernel for pure QCD - if ker_base.is_singlet: - gamma_singlet = ad.gamma_singlet(order, ker_base.n, nf) - # scale var exponentiated is directly applied on gamma - if sv_mode == sv.Modes.exponentiated: - gamma_singlet = sv.exponentiated.gamma_variation( - gamma_singlet, order, nf, L - ) - ker = s.dispatcher( - order, - method, - gamma_singlet, - as1, - as0, - nf, - ev_op_iterations, - ev_op_max_order, - ) - # scale var expanded is applied on the kernel - if sv_mode == sv.Modes.expanded and not is_threshold: - ker = np.ascontiguousarray( - sv.expanded.singlet_variation(gamma_singlet, as_raw, order, nf, L) - ) @ np.ascontiguousarray(ker) - ker = select_singlet_element(ker, mode0, mode1) - else: - gamma_ns = ad.gamma_ns(order, mode0, ker_base.n, nf) - if sv_mode == sv.Modes.exponentiated: - gamma_ns = sv.exponentiated.gamma_variation(gamma_ns, order, nf, L) - ker = ns.dispatcher( - order, - method, - gamma_ns, - as1, - as0, - nf, - ev_op_iterations, + ker = quad_ker_qcd( + ker_base, + order, + mode0, + mode1, + method, + as1, + as0, + as_raw, + nf, + L, + ev_op_iterations, + ev_op_max_order, + sv_mode, + is_threshold, + ) + else: + ker = quad_ker_qed( + ker_base, + order, + mode0, + mode1, + method, + as1, + as0, + as_raw, + aem_list, + alphaem_running, + nf, + L, + ev_op_iterations, + ev_op_max_order, + sv_mode, + is_threshold, + ) + + # recombine everything + return np.real(ker * integrand) + + +@nb.njit(cache=True) +def quad_ker_qcd( + ker_base, + order, + mode0, + mode1, + method, + as1, + as0, + as_raw, + nf, + L, + ev_op_iterations, + ev_op_max_order, + sv_mode, + is_threshold, +): + """Raw evolution kernel inside quad. + + Parameters + ---------- + quad_ker : float + quad argument + order : int + perturbation order + mode0: int + pid for first sector element + mode1 : int + pid for second sector element + method : str + method + as1 : float + target coupling value + as0 : float + initial coupling value + as_raw : float + coupling value at the process scale + nf : int + number of active flavors + L : float + logarithm of the squared ratio of factorization and renormalization scale + ev_op_iterations : int + number of evolution steps + ev_op_max_order : int + perturbative expansion order of U + sv_mode: int, `enum.IntEnum` + scale variation mode, see `eko.scale_variations.Modes` + is_threshold : boolean + is this an itermediate threshold operator? + + Returns + ------- + float + evaluated integration kernel + """ + # compute the actual evolution kernel for pure QCD + if ker_base.is_singlet: + gamma_singlet = ad.gamma_singlet(order, ker_base.n, nf) + # scale var exponentiated is directly applied on gamma + if sv_mode == sv.Modes.exponentiated: + gamma_singlet = sv.exponentiated.gamma_variation( + gamma_singlet, order, nf, L ) - if sv_mode == sv.Modes.expanded and not is_threshold: - ker = ( - sv.expanded.non_singlet_variation(gamma_ns, as_raw, order, nf, L) - * ker - ) + ker = s.dispatcher( + order, + method, + gamma_singlet, + as1, + as0, + nf, + ev_op_iterations, + ev_op_max_order, + ) + # scale var expanded is applied on the kernel + if sv_mode == sv.Modes.expanded and not is_threshold: + ker = np.ascontiguousarray( + sv.expanded.singlet_variation(gamma_singlet, as_raw, order, nf, L) + ) @ np.ascontiguousarray(ker) + ker = select_singlet_element(ker, mode0, mode1) else: - # compute the actual evolution kernel for QEDxQCD - if ker_base.is_QEDsinglet: - gamma_s = ad.gamma_singlet_qed(order, ker_base.n, nf) - # scale var exponentiated is directly applied on gamma - if sv_mode == sv.Modes.exponentiated: - gamma_s = sv.exponentiated.gamma_variation_qed( - gamma_s, order, nf, L, alphaem_running - ) - ker = qed_s.dispatcher( - order, - method, - gamma_s, - as1, - as0, - aem_list, - nf, - ev_op_iterations, - ev_op_max_order, + gamma_ns = ad.gamma_ns(order, mode0, ker_base.n, nf) + if sv_mode == sv.Modes.exponentiated: + gamma_ns = sv.exponentiated.gamma_variation(gamma_ns, order, nf, L) + ker = ns.dispatcher( + order, + method, + gamma_ns, + as1, + as0, + nf, + ev_op_iterations, + ) + if sv_mode == sv.Modes.expanded and not is_threshold: + ker = ( + sv.expanded.non_singlet_variation(gamma_ns, as_raw, order, nf, L) * ker ) - # scale var expanded is applied on the kernel - # TODO : check as_raw and a_em in expanded scale variations - if sv_mode == sv.Modes.expanded and not is_threshold: - ker = np.ascontiguousarray(ker) @ np.ascontiguousarray( - sv.expanded.QEDsinglet_variation( - gamma_s, as_raw, aem_list[-1], alphaem_running, order, nf, L - ) - ) - ker = select_QEDsinglet_element(ker, mode0, mode1) - elif ker_base.is_QEDvalence: - gamma_v = ad.gamma_valence_qed(order, ker_base.n, nf) - # scale var exponentiated is directly applied on gamma - if sv_mode == sv.Modes.exponentiated: - gamma_v = sv.exponentiated.gamma_variation_qed( - gamma_v, order, nf, L, alphaem_running + return ker + + +@nb.njit(cache=True) +def quad_ker_qed( + ker_base, + order, + mode0, + mode1, + method, + as1, + as0, + as_raw, + aem_list, + alphaem_running, + nf, + L, + ev_op_iterations, + ev_op_max_order, + sv_mode, + is_threshold, +): + """Raw evolution kernel inside quad. + + Parameters + ---------- + ker_base : QuadKerBase + quad argument + order : int + perturbation order + mode0: int + pid for first sector element + mode1 : int + pid for second sector element + method : str + method + as1 : float + target coupling value + as0 : float + initial coupling value + as_raw : float + coupling value at the process scale + aem : float + electromagnetic coupling value + nf : int + number of active flavors + L : float + logarithm of the squared ratio of factorization and renormalization scale + ev_op_iterations : int + number of evolution steps + ev_op_max_order : int + perturbative expansion order of U + sv_mode: int, `enum.IntEnum` + scale variation mode, see `eko.scale_variations.Modes` + is_threshold : boolean + is this an itermediate threshold operator? + + Returns + ------- + float + evaluated integration kernel + """ + # compute the actual evolution kernel for QEDxQCD + if ker_base.is_QEDsinglet: + gamma_s = ad.gamma_singlet_qed(order, ker_base.n, nf) + # scale var exponentiated is directly applied on gamma + if sv_mode == sv.Modes.exponentiated: + gamma_s = sv.exponentiated.gamma_variation_qed( + gamma_s, order, nf, L, alphaem_running + ) + ker = qed_s.dispatcher( + order, + method, + gamma_s, + as1, + as0, + aem_list, + nf, + ev_op_iterations, + ev_op_max_order, + ) + # scale var expanded is applied on the kernel + # TODO : check as_raw and a_em in expanded scale variations + if sv_mode == sv.Modes.expanded and not is_threshold: + ker = np.ascontiguousarray(ker) @ np.ascontiguousarray( + sv.expanded.QEDsinglet_variation( + gamma_s, as_raw, aem_list[-1], alphaem_running, order, nf, L ) - ker = qed_v.dispatcher( - order, - method, - gamma_v, - as1, - as0, - aem_list, - nf, - ev_op_iterations, - ev_op_max_order, ) - # scale var expanded is applied on the kernel - if sv_mode == sv.Modes.expanded and not is_threshold: - ker = np.ascontiguousarray( - sv.expanded.QEDvalence_variation( - gamma_v, as_raw, aem_list[-1], alphaem_running, order, nf, L - ) - ) @ np.ascontiguousarray(ker) - ker = select_QEDvalence_element(ker, mode0, mode1) - else: - gamma_ns = ad.gamma_ns_qed(order, mode0, ker_base.n, nf) - # scale var exponentiated is directly applied on gamma - if sv_mode == sv.Modes.exponentiated: - gamma_ns = sv.exponentiated.gamma_variation_qed( - gamma_ns, order, nf, L, alphaem_running + ker = select_QEDsinglet_element(ker, mode0, mode1) + elif ker_base.is_QEDvalence: + gamma_v = ad.gamma_valence_qed(order, ker_base.n, nf) + # scale var exponentiated is directly applied on gamma + if sv_mode == sv.Modes.exponentiated: + gamma_v = sv.exponentiated.gamma_variation_qed( + gamma_v, order, nf, L, alphaem_running + ) + ker = qed_v.dispatcher( + order, + method, + gamma_v, + as1, + as0, + aem_list, + nf, + ev_op_iterations, + ev_op_max_order, + ) + # scale var expanded is applied on the kernel + if sv_mode == sv.Modes.expanded and not is_threshold: + ker = np.ascontiguousarray( + sv.expanded.QEDvalence_variation( + gamma_v, as_raw, aem_list[-1], alphaem_running, order, nf, L ) - ker = qed_ns.dispatcher( - order, - method, - gamma_ns, - as1, - as0, - aem_list, - alphaem_running, - nf, - ev_op_iterations, + ) @ np.ascontiguousarray(ker) + ker = select_QEDvalence_element(ker, mode0, mode1) + else: + gamma_ns = ad.gamma_ns_qed(order, mode0, ker_base.n, nf) + # scale var exponentiated is directly applied on gamma + if sv_mode == sv.Modes.exponentiated: + gamma_ns = sv.exponentiated.gamma_variation_qed( + gamma_ns, order, nf, L, alphaem_running ) - if sv_mode == sv.Modes.expanded and not is_threshold: - ker = ( - sv.expanded.QEDnon_singlet_variation( - gamma_ns, as_raw, aem_list[-1], alphaem_running, order, nf, L - ) - * ker + ker = qed_ns.dispatcher( + order, + method, + gamma_ns, + as1, + as0, + aem_list, + alphaem_running, + nf, + ev_op_iterations, + ) + if sv_mode == sv.Modes.expanded and not is_threshold: + ker = ( + sv.expanded.QEDnon_singlet_variation( + gamma_ns, as_raw, aem_list[-1], alphaem_running, order, nf, L ) - - # recombine everything - return np.real(ker * integrand) + * ker + ) + return ker class Operator(sv.ModeMixin): From 2ca09f9ecfd0602a9937d9de2335e53774fc783e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 29 Nov 2022 12:22:30 +0100 Subject: [PATCH 249/312] Fix docstrings in anomalous dimensions --- src/eko/anomalous_dimensions/aem1.py | 20 +++++++++++--------- src/eko/anomalous_dimensions/as1.py | 14 ++++++++------ src/eko/anomalous_dimensions/as2.py | 20 ++++++++++++-------- src/eko/anomalous_dimensions/as3.py | 20 ++++++++++++-------- 4 files changed, 43 insertions(+), 31 deletions(-) diff --git a/src/eko/anomalous_dimensions/aem1.py b/src/eko/anomalous_dimensions/aem1.py index fb0c2a3c0..a7183a691 100644 --- a/src/eko/anomalous_dimensions/aem1.py +++ b/src/eko/anomalous_dimensions/aem1.py @@ -22,7 +22,7 @@ def gamma_phq(N): Returns ------- gamma_phq : complex - Leading-order photon-quark anomalous dimension :math:`\\gamma_{\\gamma q}^{(0)}(N)` + Leading-order photon-quark anomalous dimension :math:`\\gamma_{\\gamma q}^{(0,1)}(N)` """ return as1.gamma_gq(N) / constants.CF @@ -46,7 +46,7 @@ def gamma_qph(N, nf): Returns ------- gamma_qph : complex - Leading-order quark-photon anomalous dimension :math:`\\gamma_{q \\gamma}^{(0)}(N)` + Leading-order quark-photon anomalous dimension :math:`\\gamma_{q \\gamma}^{(0,1)}(N)` """ return as1.gamma_qg(N, nf) / constants.TR * constants.NC @@ -66,7 +66,7 @@ def gamma_phph(nf): Returns ------- gamma_phph : complex - Leading-order phton-photon anomalous dimension :math:`\\gamma_{\\gamma \\gamma}^{(0)}(N)` + Leading-order phton-photon anomalous dimension :math:`\\gamma_{\\gamma \\gamma}^{(0,1)}(N)` """ nu = constants.uplike_flavors(nf) nd = nf - nu @@ -90,7 +90,7 @@ def gamma_ns(N, sx): Returns ------- gamma_ns : complex - Leading-order non-singlet QED anomalous dimension :math:`\\gamma_{ns}^{(0)}(N)` + Leading-order non-singlet QED anomalous dimension :math:`\\gamma_{ns}^{(0,1)}(N)` """ s1 = sx[0] return as1.gamma_ns(N, s1) / constants.CF @@ -103,8 +103,10 @@ def gamma_singlet(N, nf, sx): .. math:: \gamma_S^{(0)} = \left(\begin{array}{cc} - \gamma_{qq}^{(0)} & \gamma_{qg}^{(0)}\\ - \gamma_{gq}^{(0)} & \gamma_{gg}^{(0)} + 0 & 0 & 0 & 0 \\ + 0 & \gamma_{\gamma \gamma}^{(0,1)} & \langle e^2 \rangle \gamma_{\gamma q}^{(0,1)} & \nu_u e^2_- \gamma_{\gamma q}^{(0,1)}\\ + 0 & \langle e^2 \rangle\gamma_{q \gamma}^{(0,1)} & \langle e^2 \rangle \gamma_{ns}^{(0,1)} & \nu_u e^2_- \gamma_{ns}^{(0,1)}\\ + 0 & \nu_d e^2_- \gamma_{q \gamma}^{(0,1)} & \nu_d e^2_- \gamma_{ns}^{(0,1)} & e^2_\Delta \gamma_{ns}^{(0,1)} \end{array}\right) Parameters @@ -168,9 +170,9 @@ def gamma_valence(N, nf, sx): Compute the leading-order valence anomalous dimension matrix. .. math:: - \gamma_S^{(0)} = \left(\begin{array}{cc} - \gamma_{qq}^{(0)} & \gamma_{qg}^{(0)}\\ - \gamma_{gq}^{(0)} & \gamma_{gg}^{(0)} + \gamma_V^{(0,1)} = \left(\begin{array}{cc} + \langle e^2 \rangle \gamma_{ns}^{(0,1)} & \nu_u e^2_- \gamma_{ns}^{(0,1)}\\ + \nu_d e^2_- \gamma_{ns}^{(0,1)} & e^2_\Delta \gamma_{ns}^{(0,1)} \end{array}\right) Parameters diff --git a/src/eko/anomalous_dimensions/as1.py b/src/eko/anomalous_dimensions/as1.py index 181943d32..bf266066d 100644 --- a/src/eko/anomalous_dimensions/as1.py +++ b/src/eko/anomalous_dimensions/as1.py @@ -147,9 +147,11 @@ def gamma_singlet_qed(N, s1, nf): Compute the leading-order singlet anomalous dimension matrix. .. math:: - \gamma_S^{(0)} = \left(\begin{array}{cc} - \gamma_{qq}^{(0)} & \gamma_{qg}^{(0)}\\ - \gamma_{gq}^{(0)} & \gamma_{gg}^{(0)} + \gamma_S^{(1,0)} = \left(\begin{array}{cccc} + \gamma_{gg}^{(1,0)} & 0 & \gamma_{gq}^{(1,0)} & 0\\ + 0 & 0 & 0 & 0 \\ + \gamma_{qg}^{(1,0)} & 0 & \gamma_{qq}^{(1,0)} & 0 \\ + 0 & 0 & 0 & \gamma_{qq}^{(1,0)} \\ \end{array}\right) Parameters @@ -192,9 +194,9 @@ def gamma_valence_qed(N, s1): Compute the leading-order valence anomalous dimension matrix. .. math:: - \gamma_S^{(0)} = \left(\begin{array}{cc} - \gamma_{qq}^{(0)} & \gamma_{qg}^{(0)}\\ - \gamma_{gq}^{(0)} & \gamma_{gg}^{(0)} + \gamma_V^{(1,0)} = \left(\begin{array}{cc} + \gamma_{ns}^{(1,0)} & 0\\ + 0 & \gamma_{ns}^{(1,0)} \end{array}\right) Parameters diff --git a/src/eko/anomalous_dimensions/as2.py b/src/eko/anomalous_dimensions/as2.py index 8ba8f8666..cfea2c9f0 100644 --- a/src/eko/anomalous_dimensions/as2.py +++ b/src/eko/anomalous_dimensions/as2.py @@ -281,19 +281,21 @@ def gamma_singlet_qed(N, nf, sx): Compute the leading-order singlet anomalous dimension matrix. .. math:: - \gamma_S^{(0)} = \left(\begin{array}{cc} - \gamma_{qq}^{(0)} & \gamma_{qg}^{(0)}\\ - \gamma_{gq}^{(0)} & \gamma_{gg}^{(0)} + \gamma_S^{(2,0)} = \left(\begin{array}{cccc} + \gamma_{gg}^{(2,0)} & 0 & \gamma_{gq}^{(2,0)} & 0\\ + 0 & 0 & 0 & 0 \\ + \gamma_{qg}^{(2,0)} & 0 & \gamma_{qq}^{(2,0)} & 0 \\ + 0 & 0 & 0 & \gamma_{qq}^{(2,0)} \\ \end{array}\right) Parameters ---------- N : complex Mellin moment - s1 : complex - harmonic sum :math:`S_{1}` nf : int Number of active flavors + s1 : complex + harmonic sum :math:`S_{1}` Returns ------- @@ -327,15 +329,17 @@ def gamma_valence_qed(N, nf, sx): Compute the leading-order valence anomalous dimension matrix. .. math:: - \gamma_S^{(0)} = \left(\begin{array}{cc} - \gamma_{qq}^{(0)} & \gamma_{qg}^{(0)}\\ - \gamma_{gq}^{(0)} & \gamma_{gg}^{(0)} + \gamma_V^{(2,0)} = \left(\begin{array}{cc} + \gamma_{ns-}^{(2,0)} & 0\\ + 0 & \gamma_{ns-}^{(2,0)} \end{array}\right) Parameters ---------- N : complex Mellin moment + nf : int + Number of active flavors s1 : complex harmonic sum :math:`S_{1}` diff --git a/src/eko/anomalous_dimensions/as3.py b/src/eko/anomalous_dimensions/as3.py index 93e4ad066..094f858c6 100644 --- a/src/eko/anomalous_dimensions/as3.py +++ b/src/eko/anomalous_dimensions/as3.py @@ -593,19 +593,21 @@ def gamma_singlet_qed(N, nf, sx): Compute the leading-order singlet anomalous dimension matrix. .. math:: - \gamma_S^{(0)} = \left(\begin{array}{cc} - \gamma_{qq}^{(0)} & \gamma_{qg}^{(0)}\\ - \gamma_{gq}^{(0)} & \gamma_{gg}^{(0)} + \gamma_S^{(3,0)} = \left(\begin{array}{cccc} + \gamma_{gg}^{(3,0)} & 0 & \gamma_{gq}^{(3,0)} & 0\\ + 0 & 0 & 0 & 0 \\ + \gamma_{qg}^{(3,0)} & 0 & \gamma_{qq}^{(3,0)} & 0 \\ + 0 & 0 & 0 & \gamma_{qq}^{(3,0)} \\ \end{array}\right) Parameters ---------- N : complex Mellin moment - s1 : complex - harmonic sum :math:`S_{1}` nf : int Number of active flavors + s1 : complex + harmonic sum :math:`S_{1}` Returns ------- @@ -639,15 +641,17 @@ def gamma_valence_qed(N, nf, sx): Compute the leading-order valence anomalous dimension matrix. .. math:: - \gamma_S^{(0)} = \left(\begin{array}{cc} - \gamma_{qq}^{(0)} & \gamma_{qg}^{(0)}\\ - \gamma_{gq}^{(0)} & \gamma_{gg}^{(0)} + \gamma_V^{(3,0)} = \left(\begin{array}{cc} + \gamma_{nsV}^{(3,0)} & 0\\ + 0 & \gamma_{ns-}^{(3,0)} \end{array}\right) Parameters ---------- N : complex Mellin moment + nf : int + Number of active flavors s1 : complex harmonic sum :math:`S_{1}` From c8254511f88bb3f3b0168e6767fe8694757db1a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 29 Nov 2022 12:47:19 +0100 Subject: [PATCH 250/312] Rename qed scale variations --- src/eko/couplings.py | 4 ++++ src/eko/evolution_operator/__init__.py | 6 +++--- src/eko/scale_variations/expanded.py | 6 +++--- tests/eko/test_sv_expanded.py | 6 +++--- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/eko/couplings.py b/src/eko/couplings.py index 2c7d90578..8e3d1d8fd 100644 --- a/src/eko/couplings.py +++ b/src/eko/couplings.py @@ -293,6 +293,8 @@ def couplings_expanded_alphaem_running(order, couplings_ref, nf, scale_from, sca res_aem = expanded_qed(couplings_ref[1], order[1], nf, lmu) # if order[0] >= 1 and order[1] >= 1: # order[0] is always >=1 + # TODO : implement decoupled running + # if not decoupled_running if order[1] >= 1: beta_qcd0 = beta_qcd((2, 0), nf) beta_qed0 = beta_qed((0, 2), nf) @@ -699,6 +701,7 @@ def compute_exact_fixed_alphaem(self, a_ref, nf, scale_from, scale_to): ) return np.array([rge_qcd, a_ref[1]]) + # TODO : implement decoupled running # def compute_exact_decoupled_running(self, a_ref, nf, scale_qcd_from, scale_qed_from, scale_to): # """Compute couplings via |RGE| with running alphaem without the mixed terms. @@ -829,6 +832,7 @@ def rge(_as, a_em, beta_qcd_vec, beta_qcd_mix, beta_qed_vec, beta_qed_mix): ) return res.y[0][-1] + # TODO : implement decoupled running # def compute_aem_as_decoupled(self, aem_ref, as_from, as_to, nf): # """Compute :math:`a_{em}` as a function of :math:`a_s` with no mixing terms. diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 8726b90d6..1ef5e2918 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -476,7 +476,7 @@ def quad_ker_qed( # TODO : check as_raw and a_em in expanded scale variations if sv_mode == sv.Modes.expanded and not is_threshold: ker = np.ascontiguousarray(ker) @ np.ascontiguousarray( - sv.expanded.QEDsinglet_variation( + sv.expanded.singlet_variation_qed( gamma_s, as_raw, aem_list[-1], alphaem_running, order, nf, L ) ) @@ -502,7 +502,7 @@ def quad_ker_qed( # scale var expanded is applied on the kernel if sv_mode == sv.Modes.expanded and not is_threshold: ker = np.ascontiguousarray( - sv.expanded.QEDvalence_variation( + sv.expanded.valence_variation_qed( gamma_v, as_raw, aem_list[-1], alphaem_running, order, nf, L ) ) @ np.ascontiguousarray(ker) @@ -527,7 +527,7 @@ def quad_ker_qed( ) if sv_mode == sv.Modes.expanded and not is_threshold: ker = ( - sv.expanded.QEDnon_singlet_variation( + sv.expanded.non_singlet_variation_qed( gamma_ns, as_raw, aem_list[-1], alphaem_running, order, nf, L ) * ker diff --git a/src/eko/scale_variations/expanded.py b/src/eko/scale_variations/expanded.py index 091862a57..dc0984990 100644 --- a/src/eko/scale_variations/expanded.py +++ b/src/eko/scale_variations/expanded.py @@ -173,7 +173,7 @@ def singlet_variation(gamma, a_s, order, nf, L): @nb.njit(cache=True) -def QEDnon_singlet_variation(gamma, a_s, a_em, alphaem_running, order, nf, L): +def non_singlet_variation_qed(gamma, a_s, a_em, alphaem_running, order, nf, L): """Non-singlet scale variation dispatcher. Parameters @@ -202,7 +202,7 @@ def QEDnon_singlet_variation(gamma, a_s, a_em, alphaem_running, order, nf, L): @nb.njit(cache=True) -def QEDsinglet_variation(gamma, a_s, a_em, alphaem_running, order, nf, L): +def singlet_variation_qed(gamma, a_s, a_em, alphaem_running, order, nf, L): """Singlet scale variation dispatcher. Parameters @@ -247,7 +247,7 @@ def QEDsinglet_variation(gamma, a_s, a_em, alphaem_running, order, nf, L): @nb.njit(cache=True) -def QEDvalence_variation(gamma, a_s, a_em, alphaem_running, order, nf, L): +def valence_variation_qed(gamma, a_s, a_em, alphaem_running, order, nf, L): """Singlet scale variation dispatcher. Parameters diff --git a/tests/eko/test_sv_expanded.py b/tests/eko/test_sv_expanded.py index ca00cba73..3599891d8 100644 --- a/tests/eko/test_sv_expanded.py +++ b/tests/eko/test_sv_expanded.py @@ -38,7 +38,7 @@ def test_ns_sv_dispacher_qed(): a_em = 0.01 for alphaem_running in [True, False]: np.testing.assert_allclose( - expanded.QEDnon_singlet_variation( + expanded.non_singlet_variation_qed( gamma_ns, a_s, a_em, alphaem_running, order, nf, L ), 1.0, @@ -67,7 +67,7 @@ def test_singlet_sv_dispacher_qed(): a_em = 0.01 for alphaem_running in [True, False]: np.testing.assert_allclose( - expanded.QEDsinglet_variation( + expanded.singlet_variation_qed( gamma_singlet, a_s, a_em, alphaem_running, order, nf, L ), np.eye(4), @@ -84,7 +84,7 @@ def test_valence_sv_dispacher_qed(): a_em = 0.01 for alphaem_running in [True, False]: np.testing.assert_allclose( - expanded.QEDvalence_variation( + expanded.valence_variation_qed( gamma_valence, a_s, a_em, alphaem_running, order, nf, L ), np.eye(2), From 6c0062d4029fca6c31ae1eafa44c42e9938243fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 1 Dec 2022 12:30:41 +0100 Subject: [PATCH 251/312] Enforce numpy docstring stile in anomalous_dimentsions --- src/eko/anomalous_dimensions/aem1.py | 76 ++++----- src/eko/anomalous_dimensions/aem2.py | 188 ++++++++++----------- src/eko/anomalous_dimensions/as1.py | 101 ++++++----- src/eko/anomalous_dimensions/as1aem1.py | 140 ++++++++-------- src/eko/anomalous_dimensions/as2.py | 189 ++++++++++----------- src/eko/anomalous_dimensions/as3.py | 214 +++++++++++------------- 6 files changed, 438 insertions(+), 470 deletions(-) diff --git a/src/eko/anomalous_dimensions/aem1.py b/src/eko/anomalous_dimensions/aem1.py index a7183a691..d3c8762e2 100644 --- a/src/eko/anomalous_dimensions/aem1.py +++ b/src/eko/anomalous_dimensions/aem1.py @@ -9,19 +9,18 @@ @nb.njit(cache=True) def gamma_phq(N): - r""" - Compute the leading-order photon-quark anomalous dimension. + r"""Compute the leading-order photon-quark anomalous dimension. Implements Eq. (2.5) of :cite:`Carrazza:2015dea`. Parameters ---------- - N : complex + N : complex Mellin moment Returns ------- - gamma_phq : complex + gamma_phq : complex Leading-order photon-quark anomalous dimension :math:`\\gamma_{\\gamma q}^{(0,1)}(N)` """ return as1.gamma_gq(N) / constants.CF @@ -29,8 +28,7 @@ def gamma_phq(N): @nb.njit(cache=True) def gamma_qph(N, nf): - r""" - Compute the leading-order quark-photon anomalous dimension. + r"""Compute the leading-order quark-photon anomalous dimension. Implements Eq. (2.5) of :cite:`Carrazza:2015dea`. But adding the :math:`N_C` and the :math:`2n_f` factors from :math:`\\theta` inside the @@ -38,14 +36,14 @@ def gamma_qph(N, nf): Parameters ---------- - N : complex + N : complex Mellin moment - nf : int + nf : int Number of active flavors Returns ------- - gamma_qph : complex + gamma_qph : complex Leading-order quark-photon anomalous dimension :math:`\\gamma_{q \\gamma}^{(0,1)}(N)` """ return as1.gamma_qg(N, nf) / constants.TR * constants.NC @@ -53,19 +51,18 @@ def gamma_qph(N, nf): @nb.njit(cache=True) def gamma_phph(nf): - r""" - Compute the leading-order photon-photon anomalous dimension. + r"""Compute the leading-order photon-photon anomalous dimension. Implements Eq. (2.5) of :cite:`Carrazza:2015dea`. Parameters ---------- - nf : int + nf : int Number of active flavors Returns ------- - gamma_phph : complex + gamma_phph : complex Leading-order phton-photon anomalous dimension :math:`\\gamma_{\\gamma \\gamma}^{(0,1)}(N)` """ nu = constants.uplike_flavors(nf) @@ -75,21 +72,20 @@ def gamma_phph(nf): @nb.njit(cache=True) def gamma_ns(N, sx): - r""" - Compute the leading-order non-singlet QED anomalous dimension. + r"""Compute the leading-order non-singlet QED anomalous dimension. Implements Eq. (2.5) of :cite:`Carrazza:2015dea`. Parameters ---------- - N : complex + N : complex Mellin moment - s1 : complex + s1 : complex S1(N) Returns ------- - gamma_ns : complex + gamma_ns : complex Leading-order non-singlet QED anomalous dimension :math:`\\gamma_{ns}^{(0,1)}(N)` """ s1 = sx[0] @@ -98,16 +94,15 @@ def gamma_ns(N, sx): @nb.njit(cache=True) def gamma_singlet(N, nf, sx): - r""" - Compute the leading-order singlet anomalous dimension matrix. + r"""Compute the leading-order singlet anomalous dimension matrix. .. math:: - \gamma_S^{(0)} = \left(\begin{array}{cc} + \\gamma_S^{(0)} = \\left(\begin{array}{cc} 0 & 0 & 0 & 0 \\ - 0 & \gamma_{\gamma \gamma}^{(0,1)} & \langle e^2 \rangle \gamma_{\gamma q}^{(0,1)} & \nu_u e^2_- \gamma_{\gamma q}^{(0,1)}\\ - 0 & \langle e^2 \rangle\gamma_{q \gamma}^{(0,1)} & \langle e^2 \rangle \gamma_{ns}^{(0,1)} & \nu_u e^2_- \gamma_{ns}^{(0,1)}\\ - 0 & \nu_d e^2_- \gamma_{q \gamma}^{(0,1)} & \nu_d e^2_- \gamma_{ns}^{(0,1)} & e^2_\Delta \gamma_{ns}^{(0,1)} - \end{array}\right) + 0 & \\gamma_{\\gamma \\gamma}^{(0,1)} & \\langle e^2 \rangle \\gamma_{\\gamma q}^{(0,1)} & \nu_u e^2_- \\gamma_{\\gamma q}^{(0,1)}\\ + 0 & \\langle e^2 \rangle\\gamma_{q \\gamma}^{(0,1)} & \\langle e^2 \rangle \\gamma_{ns}^{(0,1)} & \nu_u e^2_- \\gamma_{ns}^{(0,1)}\\ + 0 & \nu_d e^2_- \\gamma_{q \\gamma}^{(0,1)} & \nu_d e^2_- \\gamma_{ns}^{(0,1)} & e^2_\\Delta \\gamma_{ns}^{(0,1)} + \\end{array}\right) Parameters ---------- @@ -121,14 +116,14 @@ def gamma_singlet(N, nf, sx): Returns ------- gamma_S_0 : numpy.ndarray - Leading-order singlet anomalous dimension matrix :math:`\gamma_{S}^{(0)}(N)` + Leading-order singlet anomalous dimension matrix :math:`\\gamma_{S}^{(0)}(N)` See Also -------- - gamma_ns : :math:`\gamma_{qq}^{(0)}` - gamma_qg : :math:`\gamma_{qg}^{(0)}` - gamma_gq : :math:`\gamma_{gq}^{(0)}` - gamma_gg : :math:`\gamma_{gg}^{(0)}` + gamma_ns : :math:`\\gamma_{qq}^{(0)}` + gamma_qg : :math:`\\gamma_{qg}^{(0)}` + gamma_gq : :math:`\\gamma_{gq}^{(0)}` + gamma_gg : :math:`\\gamma_{gg}^{(0)}` """ e2avg = constants.e2avg(nf) vue2m = constants.vue2m(nf) @@ -166,14 +161,13 @@ def gamma_singlet(N, nf, sx): @nb.njit(cache=True) def gamma_valence(N, nf, sx): - r""" - Compute the leading-order valence anomalous dimension matrix. + r"""Compute the leading-order valence anomalous dimension matrix. .. math:: - \gamma_V^{(0,1)} = \left(\begin{array}{cc} - \langle e^2 \rangle \gamma_{ns}^{(0,1)} & \nu_u e^2_- \gamma_{ns}^{(0,1)}\\ - \nu_d e^2_- \gamma_{ns}^{(0,1)} & e^2_\Delta \gamma_{ns}^{(0,1)} - \end{array}\right) + \\gamma_V^{(0,1)} = \\left(\begin{array}{cc} + \\langle e^2 \rangle \\gamma_{ns}^{(0,1)} & \nu_u e^2_- \\gamma_{ns}^{(0,1)}\\ + \nu_d e^2_- \\gamma_{ns}^{(0,1)} & e^2_\\Delta \\gamma_{ns}^{(0,1)} + \\end{array}\right) Parameters ---------- @@ -185,14 +179,14 @@ def gamma_valence(N, nf, sx): Returns ------- gamma_S_0 : numpy.ndarray - Leading-order singlet anomalous dimension matrix :math:`\gamma_{S}^{(0)}(N)` + Leading-order singlet anomalous dimension matrix :math:`\\gamma_{S}^{(0)}(N)` See Also -------- - gamma_ns : :math:`\gamma_{qq}^{(0)}` - gamma_qg : :math:`\gamma_{qg}^{(0)}` - gamma_gq : :math:`\gamma_{gq}^{(0)}` - gamma_gg : :math:`\gamma_{gg}^{(0)}` + gamma_ns : :math:`\\gamma_{qq}^{(0)}` + gamma_qg : :math:`\\gamma_{qg}^{(0)}` + gamma_gq : :math:`\\gamma_{gq}^{(0)}` + gamma_gg : :math:`\\gamma_{gg}^{(0)}` """ e2avg = constants.e2avg(nf) vue2m = constants.vue2m(nf) diff --git a/src/eko/anomalous_dimensions/aem2.py b/src/eko/anomalous_dimensions/aem2.py index bb4cbc8f7..05bb033ad 100644 --- a/src/eko/anomalous_dimensions/aem2.py +++ b/src/eko/anomalous_dimensions/aem2.py @@ -15,16 +15,16 @@ def gamma_phph(N, nf): Parameters ---------- - N : complex - Mellin moment - nf : int - Number of active flavors + N : complex + Mellin moment + nf : int + Number of active flavors Returns ------- - gamma_gg : complex - :math:`O(a_{em}^2)` photon-photon singlet anomalous dimension - :math:`\\gamma_{\\gamma \\gamma}^{(0,2)}(N)` + gamma_gg : complex + :math:`O(a_{em}^2)` photon-photon singlet anomalous dimension + :math:`\\gamma_{\\gamma \\gamma}^{(0,2)}(N)` """ nu = constants.uplike_flavors(nf) @@ -44,16 +44,16 @@ def gamma_uph(N, nf, sx): Parameters ---------- - N : complex - Mellin moment - nf : int - Number of active flavors - sx : np array - List of harmonic sums + N : complex + Mellin moment + nf : int + Number of active flavors + sx : np array + List of harmonic sums Returns ------- - gamma_uph : complex + gamma_uph : complex :math:`O(a_{em}^2)` quark-photon anomalous dimension :math:`\\gamma_{u \\gamma}^{(0,2)}(N)` """ @@ -68,16 +68,16 @@ def gamma_dph(N, nf, sx): Parameters ---------- - N : complex - Mellin moment - nf : int - Number of active flavors - sx : np array - List of harmonic sums + N : complex + Mellin moment + nf : int + Number of active flavors + sx : np array + List of harmonic sums Returns ------- - gamma_dph : complex + gamma_dph : complex :math:`O(a_{em}^2)` quark-photon anomalous dimension :math:`\\gamma_{d \\gamma}^{(0,2)}(N)` """ @@ -92,16 +92,16 @@ def gamma_phu(N, nf, sx): Parameters ---------- - N : complex - Mellin moment - nf : int - Number of active flavors - sx : np array - List of harmonic sums + N : complex + Mellin moment + nf : int + Number of active flavors + sx : np array + List of harmonic sums Returns ------- - gamma_phu : complex + gamma_phu : complex :math:`O(a_{em}^2)` photon-quark anomalous dimension :math:`\\gamma_{\\gamma u}^{(0,2)}(N)` """ @@ -125,16 +125,16 @@ def gamma_phd(N, nf, sx): Parameters ---------- - N : complex - Mellin moment - nf : int - Number of active flavors - sx : np array - List of harmonic sums + N : complex + Mellin moment + nf : int + Number of active flavors + sx : np array + List of harmonic sums Returns ------- - gamma_phd : complex + gamma_phd : complex :math:`O(a_{em}^2)` photon-quark anomalous dimension :math:`\\gamma_{\\gamma d}^{(0,2)}(N)` """ @@ -158,18 +158,18 @@ def gamma_nspu(N, nf, sx, sx_ns_qed): Parameters ---------- - N : complex - Mellin moment - nf : int - Number of active flavors - sx : np array - List of harmonic sums + N : complex + Mellin moment + nf : int + Number of active flavors + sx : np array + List of harmonic sums Returns ------- - gamma_nspu : complex - :math:`O(a_{em}^2)` singlet-like non-singlet anomalous dimension - :math:`\\gamma_{ns,+,u}^{(0,2)}(N)` + gamma_nspu : complex + :math:`O(a_{em}^2)` singlet-like non-singlet anomalous dimension + :math:`\\gamma_{ns,+,u}^{(0,2)}(N)` """ S1 = sx[0] @@ -197,18 +197,18 @@ def gamma_nspd(N, nf, sx, sx_ns_qed): Parameters ---------- - N : complex - Mellin moment - nf : int - Number of active flavors - sx : np array - List of harmonic sums + N : complex + Mellin moment + nf : int + Number of active flavors + sx : np array + List of harmonic sums Returns ------- - gamma_nspd : complex - :math:`O(a_{em}^2)` singlet-like non-singlet anomalous dimension - :math:`\\gamma_{ns,+,d}^{(0,2)}(N)` + gamma_nspd : complex + :math:`O(a_{em}^2)` singlet-like non-singlet anomalous dimension + :math:`\\gamma_{ns,+,d}^{(0,2)}(N)` """ S1 = sx[0] @@ -236,18 +236,18 @@ def gamma_nsmu(N, nf, sx, sx_ns_qed): Parameters ---------- - N : complex - Mellin moment - nf : int - Number of active flavors - sx : np array - List of harmonic sums + N : complex + Mellin moment + nf : int + Number of active flavors + sx : np array + List of harmonic sums Returns ------- - gamma_nsp : complex - :math:`O(a_{em}^2)` valence-like non-singlet anomalous dimension - :math:`\\gamma_{ns,-,u}^{(0,2)}(N)` + gamma_nsp : complex + :math:`O(a_{em}^2)` valence-like non-singlet anomalous dimension + :math:`\\gamma_{ns,-,u}^{(0,2)}(N)` """ S1 = sx[0] @@ -275,18 +275,18 @@ def gamma_nsmd(N, nf, sx, sx_ns_qed): Parameters ---------- - N : complex - Mellin moment - nf : int - Number of active flavors - sx : np array - List of harmonic sums + N : complex + Mellin moment + nf : int + Number of active flavors + sx : np array + List of harmonic sums Returns ------- - gamma_nsp : complex - :math:`O(a_{em}^2)` valence-like non-singlet anomalous dimension - :math:`\\gamma_{ns,-,d}^{(0,2)}(N)` + gamma_nsp : complex + :math:`O(a_{em}^2)` valence-like non-singlet anomalous dimension + :math:`\\gamma_{ns,-,d}^{(0,2)}(N)` """ S1 = sx[0] @@ -314,16 +314,16 @@ def gamma_ps(N, nf): Parameters ---------- - N : complex - Mellin moment - nf : int - Number of active flavors + N : complex + Mellin moment + nf : int + Number of active flavors Returns ------- - gamma_ps : complex - :math:`O(a_{em}^2)` pure-singlet quark-quark anomalous dimension - :math:`\\gamma_{ps}^{(0,2)}(N)` + gamma_ps : complex + :math:`O(a_{em}^2)` pure-singlet quark-quark anomalous dimension + :math:`\\gamma_{ps}^{(0,2)}(N)` """ result = ( @@ -341,17 +341,17 @@ def gamma_singlet(N, nf, sx, sx_ns_qed): Parameters ---------- - N : complex - Mellin moment - nf : int - Number of active flavors - sx : np array - List of harmonic sums + N : complex + Mellin moment + nf : int + Number of active flavors + sx : np array + List of harmonic sums Returns ------- - gamma_singlet : numpy.ndarray - :math:`O(a_{em}^2)` singlet anomalous dimension :math:`\\gamma_{S}^{(0,2)}(N,nf,sx)` + gamma_singlet : numpy.ndarray + :math:`O(a_{em}^2)` singlet anomalous dimension :math:`\\gamma_{S}^{(0,2)}(N,nf,sx)` """ nu = constants.uplike_flavors(nf) nd = nf - nu @@ -413,17 +413,17 @@ def gamma_valence(N, nf, sx, sx_ns_qed): Parameters ---------- - N : complex - Mellin moment - nf : int - Number of active flavors - sx : np array - List of harmonic sums + N : complex + Mellin moment + nf : int + Number of active flavors + sx : np array + List of harmonic sums Returns ------- - gamma_singlet : numpy.ndarray - :math:`O(a_{em}^2)` valence anomalous dimension :math:`\\gamma_{V}^{(0,2)}(N,nf,sx)` + gamma_singlet : numpy.ndarray + :math:`O(a_{em}^2)` valence anomalous dimension :math:`\\gamma_{V}^{(0,2)}(N,nf,sx)` """ nu = constants.uplike_flavors(nf) nd = nf - nu diff --git a/src/eko/anomalous_dimensions/as1.py b/src/eko/anomalous_dimensions/as1.py index bf266066d..714a42e0e 100644 --- a/src/eko/anomalous_dimensions/as1.py +++ b/src/eko/anomalous_dimensions/as1.py @@ -8,21 +8,20 @@ @nb.njit(cache=True) def gamma_ns(N, s1): - r""" - Compute the leading-order non-singlet anomalous dimension. + r"""Compute the leading-order non-singlet anomalous dimension. Implements Eq. (3.4) of :cite:`Moch:2004pa`. Parameters ---------- - N : complex + N : complex Mellin moment - s1 : complex + s1 : complex harmonic sum :math:`S_{1}` Returns ------- - gamma_ns : complex + gamma_ns : complex Leading-order non-singlet anomalous dimension :math:`\\gamma_{ns}^{(0)}(N)` """ gamma = -(3.0 - 4.0 * s1 + 2.0 / N / (N + 1.0)) @@ -32,21 +31,20 @@ def gamma_ns(N, s1): @nb.njit(cache=True) def gamma_qg(N, nf): - r""" - Compute the leading-order quark-gluon anomalous dimension. + r"""Compute the leading-order quark-gluon anomalous dimension. Implements Eq. (3.5) of :cite:`Vogt:2004mw`. Parameters ---------- - N : complex + N : complex Mellin moment - nf : int + nf : int Number of active flavors Returns ------- - gamma_qg : complex + gamma_qg : complex Leading-order quark-gluon anomalous dimension :math:`\\gamma_{qg}^{(0)}(N)` """ gamma = -(N**2 + N + 2.0) / (N * (N + 1.0) * (N + 2.0)) @@ -56,19 +54,18 @@ def gamma_qg(N, nf): @nb.njit(cache=True) def gamma_gq(N): - r""" - Compute the leading-order gluon-quark anomalous dimension. + r"""Compute the leading-order gluon-quark anomalous dimension. Implements Eq. (3.5) of :cite:`Vogt:2004mw`. Parameters ---------- - N : complex + N : complex Mellin moment Returns ------- - gamma_gq : complex + gamma_gq : complex Leading-order gluon-quark anomalous dimension :math:`\\gamma_{gq}^{(0)}(N)` """ gamma = -(N**2 + N + 2.0) / (N * (N + 1.0) * (N - 1.0)) @@ -78,23 +75,22 @@ def gamma_gq(N): @nb.njit(cache=True) def gamma_gg(N, s1, nf): - r""" - Compute the leading-order gluon-gluon anomalous dimension. + r"""Compute the leading-order gluon-gluon anomalous dimension. Implements Eq. (3.5) of :cite:`Vogt:2004mw`. Parameters ---------- - N : complex + N : complex Mellin moment - s1 : complex + s1 : complex harmonic sum :math:`S_{1}` - nf : int + nf : int Number of active flavors Returns ------- - gamma_gg : complex + gamma_gg : complex Leading-order gluon-gluon anomalous dimension :math:`\\gamma_{gg}^{(0)}(N)` """ gamma = s1 - 1.0 / N / (N - 1.0) - 1.0 / (N + 1.0) / (N + 2.0) @@ -104,14 +100,13 @@ def gamma_gg(N, s1, nf): @nb.njit(cache=True) def gamma_singlet(N, s1, nf): - r""" - Compute the leading-order singlet anomalous dimension matrix. + r"""Compute the leading-order singlet anomalous dimension matrix. .. math:: - \gamma_S^{(0)} = \left(\begin{array}{cc} - \gamma_{qq}^{(0)} & \gamma_{qg}^{(0)}\\ - \gamma_{gq}^{(0)} & \gamma_{gg}^{(0)} - \end{array}\right) + \\gamma_S^{(0)} = \\left(\begin{array}{cc} + \\gamma_{qq}^{(0)} & \\gamma_{qg}^{(0)}\\ + \\gamma_{gq}^{(0)} & \\gamma_{gg}^{(0)} + \\end{array}\right) Parameters ---------- @@ -125,14 +120,14 @@ def gamma_singlet(N, s1, nf): Returns ------- gamma_S_0 : numpy.ndarray - Leading-order singlet anomalous dimension matrix :math:`\gamma_{S}^{(0)}(N)` + Leading-order singlet anomalous dimension matrix :math:`\\gamma_{S}^{(0)}(N)` See Also -------- - gamma_ns : :math:`\gamma_{qq}^{(0)}` - gamma_qg : :math:`\gamma_{qg}^{(0)}` - gamma_gq : :math:`\gamma_{gq}^{(0)}` - gamma_gg : :math:`\gamma_{gg}^{(0)}` + gamma_ns : :math:`\\gamma_{qq}^{(0)}` + gamma_qg : :math:`\\gamma_{qg}^{(0)}` + gamma_gq : :math:`\\gamma_{gq}^{(0)}` + gamma_gg : :math:`\\gamma_{gg}^{(0)}` """ gamma_qq = gamma_ns(N, s1) gamma_S_0 = np.array( @@ -143,16 +138,15 @@ def gamma_singlet(N, s1, nf): @nb.njit(cache=True) def gamma_singlet_qed(N, s1, nf): - r""" - Compute the leading-order singlet anomalous dimension matrix. + r"""Compute the leading-order singlet anomalous dimension matrix. .. math:: - \gamma_S^{(1,0)} = \left(\begin{array}{cccc} - \gamma_{gg}^{(1,0)} & 0 & \gamma_{gq}^{(1,0)} & 0\\ + \\gamma_S^{(1,0)} = \\left(\begin{array}{cccc} + \\gamma_{gg}^{(1,0)} & 0 & \\gamma_{gq}^{(1,0)} & 0\\ 0 & 0 & 0 & 0 \\ - \gamma_{qg}^{(1,0)} & 0 & \gamma_{qq}^{(1,0)} & 0 \\ - 0 & 0 & 0 & \gamma_{qq}^{(1,0)} \\ - \end{array}\right) + \\gamma_{qg}^{(1,0)} & 0 & \\gamma_{qq}^{(1,0)} & 0 \\ + 0 & 0 & 0 & \\gamma_{qq}^{(1,0)} \\ + \\end{array}\right) Parameters ---------- @@ -166,14 +160,14 @@ def gamma_singlet_qed(N, s1, nf): Returns ------- gamma_S_0 : numpy.ndarray - Leading-order singlet anomalous dimension matrix :math:`\gamma_{S}^{(0)}(N)` + Leading-order singlet anomalous dimension matrix :math:`\\gamma_{S}^{(0)}(N)` See Also -------- - gamma_ns : :math:`\gamma_{qq}^{(0)}` - gamma_qg : :math:`\gamma_{qg}^{(0)}` - gamma_gq : :math:`\gamma_{gq}^{(0)}` - gamma_gg : :math:`\gamma_{gg}^{(0)}` + gamma_ns : :math:`\\gamma_{qq}^{(0)}` + gamma_qg : :math:`\\gamma_{qg}^{(0)}` + gamma_gq : :math:`\\gamma_{gq}^{(0)}` + gamma_gg : :math:`\\gamma_{gg}^{(0)}` """ gamma_qq = gamma_ns(N, s1) gamma_S = np.array( @@ -190,14 +184,13 @@ def gamma_singlet_qed(N, s1, nf): @nb.njit(cache=True) def gamma_valence_qed(N, s1): - r""" - Compute the leading-order valence anomalous dimension matrix. + r"""Compute the leading-order valence anomalous dimension matrix. .. math:: - \gamma_V^{(1,0)} = \left(\begin{array}{cc} - \gamma_{ns}^{(1,0)} & 0\\ - 0 & \gamma_{ns}^{(1,0)} - \end{array}\right) + \\gamma_V^{(1,0)} = \\left(\begin{array}{cc} + \\gamma_{ns}^{(1,0)} & 0\\ + 0 & \\gamma_{ns}^{(1,0)} + \\end{array}\right) Parameters ---------- @@ -209,14 +202,14 @@ def gamma_valence_qed(N, s1): Returns ------- gamma_S_0 : numpy.ndarray - Leading-order singlet anomalous dimension matrix :math:`\gamma_{S}^{(0)}(N)` + Leading-order singlet anomalous dimension matrix :math:`\\gamma_{S}^{(0)}(N)` See Also -------- - gamma_ns : :math:`\gamma_{qq}^{(0)}` - gamma_qg : :math:`\gamma_{qg}^{(0)}` - gamma_gq : :math:`\gamma_{gq}^{(0)}` - gamma_gg : :math:`\gamma_{gg}^{(0)}` + gamma_ns : :math:`\\gamma_{qq}^{(0)}` + gamma_qg : :math:`\\gamma_{qg}^{(0)}` + gamma_gq : :math:`\\gamma_{gq}^{(0)}` + gamma_gg : :math:`\\gamma_{gg}^{(0)}` """ gamma_V = np.array( [ diff --git a/src/eko/anomalous_dimensions/as1aem1.py b/src/eko/anomalous_dimensions/as1aem1.py index 7b8eaa442..f3f9b8ba6 100644 --- a/src/eko/anomalous_dimensions/as1aem1.py +++ b/src/eko/anomalous_dimensions/as1aem1.py @@ -15,14 +15,14 @@ def gamma_phq(N, sx): Parameters ---------- - N : complex - Mellin moment - sx : np array - List of harmonic sums + N : complex + Mellin moment + sx : np array + List of harmonic sums Returns ------- - gamma_phq : complex + gamma_phq : complex :math:`O(a_s^1a_{em}^1)` photon-quark anomalous dimension :math:`\\gamma_{\\gamma q}^{(1,1)}(N)` """ @@ -60,16 +60,16 @@ def gamma_qph(N, nf, sx): Parameters ---------- - N : complex - Mellin moment - nf : int - Number of active flavors - sx : np array - List of harmonic sums + N : complex + Mellin moment + nf : int + Number of active flavors + sx : np array + List of harmonic sums Returns ------- - gamma_qph : complex + gamma_qph : complex :math:`O(a_s^1a_{em}^1)` quark-photon anomalous dimension :math:`\\gamma_{q \\gamma}^{(1,1)}(N)` """ @@ -108,12 +108,12 @@ def gamma_gph(N): Parameters ---------- - N : complex + N : complex Mellin moment Returns ------- - gamma_qph : complex + gamma_qph : complex :math:`O(a_s^1a_{em}^1)` gluon-photon anomalous dimension :math:`\\gamma_{g \\gamma}^{(1,1)}(N)` """ @@ -139,12 +139,12 @@ def gamma_phg(N): Parameters ---------- - N : complex + N : complex Mellin moment Returns ------- - gamma_qph : complex + gamma_qph : complex :math:`O(a_s^1a_{em}^1)` photon-gluon anomalous dimension :math:`\\gamma_{\\gamma g}^{(1,1)}(N)` """ @@ -159,18 +159,18 @@ def gamma_qg(N, nf, sx): Parameters ---------- - N : complex - Mellin moment - nf : int - Number of active flavors - sx : np array - List of harmonic sums + N : complex + Mellin moment + nf : int + Number of active flavors + sx : np array + List of harmonic sums Returns ------- - gamma_qg : complex - :math:`O(a_s^1a_{em}^1)` quark-gluon singlet anomalous dimension - :math:`\\gamma_{qg}^{(1,1)}(N)` + gamma_qg : complex + :math:`O(a_s^1a_{em}^1)` quark-gluon singlet anomalous dimension + :math:`\\gamma_{qg}^{(1,1)}(N)` """ return ( @@ -186,16 +186,16 @@ def gamma_gq(N, sx): Parameters ---------- - N : complex - Mellin moment - sx : np array - List of harmonic sums + N : complex + Mellin moment + sx : np array + List of harmonic sums Returns ------- - gamma_gq : complex - :math:`O(a_s^1a_{em}^1)` gluon-quark singlet anomalous dimension - :math:`\\gamma_{gq}^{(1,1)}(N)` + gamma_gq : complex + :math:`O(a_s^1a_{em}^1)` gluon-quark singlet anomalous dimension + :math:`\\gamma_{gq}^{(1,1)}(N)` """ return gamma_phq(N, sx) @@ -209,14 +209,14 @@ def gamma_phph(nf): Parameters ---------- - nf : int - Number of active flavors + nf : int + Number of active flavors Returns ------- - gamma_gg : complex - :math:`O(a_s^1a_{em}^1)` photon-photon singlet anomalous dimension - :math:`\\gamma_{\\gamma \\gamma}^{(1,1)}(N)` + gamma_gg : complex + :math:`O(a_s^1a_{em}^1)` photon-photon singlet anomalous dimension + :math:`\\gamma_{\\gamma \\gamma}^{(1,1)}(N)` """ nu = constants.uplike_flavors(nf) @@ -232,9 +232,9 @@ def gamma_gg(): Returns ------- - gamma_gg : complex - :math:`O(a_s^1a_{em}^1)` gluon-gluon singlet anomalous dimension - :math:`\\gamma_{gg}^{(1,1)}(N)` + gamma_gg : complex + :math:`O(a_s^1a_{em}^1)` gluon-gluon singlet anomalous dimension + :math:`\\gamma_{gg}^{(1,1)}(N)` """ return 4.0 * constants.TR * constants.NC @@ -248,16 +248,16 @@ def gamma_nsp(N, sx, sx_ns_qed): Parameters ---------- - N : complex - Mellin moment - sx : np array - List of harmonic sums + N : complex + Mellin moment + sx : np array + List of harmonic sums Returns ------- - gamma_nsp : complex - :math:`O(a_s^1a_{em}^1)` singlet-like non-singlet anomalous dimension - :math:`\\gamma_{ns,+}^{(1)}(N)` + gamma_nsp : complex + :math:`O(a_s^1a_{em}^1)` singlet-like non-singlet anomalous dimension + :math:`\\gamma_{ns,+}^{(1)}(N)` """ S1 = sx[0] @@ -315,16 +315,16 @@ def gamma_nsm(N, sx, sx_ns_qed): Parameters ---------- - N : complex - Mellin moment - sx : np array - List of harmonic sums + N : complex + Mellin moment + sx : np array + List of harmonic sums Returns ------- - gamma_nsm : complex - :math:`O(a_s^1a_{em}^1)` singlet-like non-singlet anomalous dimension - :math:`\\gamma_{ns,-}^{(1,1)}(N)` + gamma_nsm : complex + :math:`O(a_s^1a_{em}^1)` singlet-like non-singlet anomalous dimension + :math:`\\gamma_{ns,-}^{(1,1)}(N)` """ S1 = sx[0] @@ -376,17 +376,17 @@ def gamma_singlet(N, nf, sx, sx_ns_qed): Parameters ---------- - N : complex - Mellin moment - nf : int - Number of active flavors - sx : np array - List of harmonic sums + N : complex + Mellin moment + nf : int + Number of active flavors + sx : np array + List of harmonic sums Returns ------- - gamma_singlet : numpy.ndarray - :math:`O(a_s^1a_{em}^1)` singlet anomalous dimension :math:`\\gamma_{S}^{(1,1)}(N,nf,sx)` + gamma_singlet : numpy.ndarray + :math:`O(a_s^1a_{em}^1)` singlet anomalous dimension :math:`\\gamma_{S}^{(1,1)}(N,nf,sx)` """ e2avg = constants.e2avg(nf) vue2m = constants.vue2m(nf) @@ -436,17 +436,17 @@ def gamma_valence(N, nf, sx, sx_ns_qed): Parameters ---------- - N : complex - Mellin moment - nf : int - Number of active flavors - sx : np array - List of harmonic sums + N : complex + Mellin moment + nf : int + Number of active flavors + sx : np array + List of harmonic sums Returns ------- - gamma_singlet : numpy.ndarray - :math:`O(a_s^1a_{em}^1)` valence anomalous dimension :math:`\\gamma_{V}^{(1,1)}(N,nf,sx)` + gamma_singlet : numpy.ndarray + :math:`O(a_s^1a_{em}^1)` valence anomalous dimension :math:`\\gamma_{V}^{(1,1)}(N,nf,sx)` """ e2avg = constants.e2avg(nf) vue2m = constants.vue2m(nf) diff --git a/src/eko/anomalous_dimensions/as2.py b/src/eko/anomalous_dimensions/as2.py index cfea2c9f0..d8fd2271b 100644 --- a/src/eko/anomalous_dimensions/as2.py +++ b/src/eko/anomalous_dimensions/as2.py @@ -14,25 +14,24 @@ @nb.njit(cache=True) def gamma_nsm(n, nf, sx): - r""" - Compute the |NLO| valence-like non-singlet anomalous dimension. + r"""Compute the |NLO| valence-like non-singlet anomalous dimension. Implements Eq. (3.5) of :cite:`Moch:2004pa`. Parameters ---------- - n : complex - Mellin moment - nf : int - Number of active flavors - sx : numpy.ndarray - List of harmonic sums: :math:`S_{1},S_{2}` + n : complex + Mellin moment + nf : int + Number of active flavors + sx : numpy.ndarray + List of harmonic sums: :math:`S_{1},S_{2}` Returns ------- - gamma_nsm : complex - |NLO| valence-like non-singlet anomalous dimension - :math:`\\gamma_{ns,-}^{(1)}(N)` + gamma_nsm : complex + |NLO| valence-like non-singlet anomalous dimension + :math:`\\gamma_{ns,-}^{(1)}(N)` """ S1 = sx[0] S2 = sx[1] @@ -57,25 +56,24 @@ def gamma_nsm(n, nf, sx): @nb.njit(cache=True) def gamma_nsp(n, nf, sx): - r""" - Compute the |NLO| singlet-like non-singlet anomalous dimension. + r"""Compute the |NLO| singlet-like non-singlet anomalous dimension. Implements Eq. (3.5) of :cite:`Moch:2004pa`. Parameters ---------- - n : complex - Mellin moment - nf : int - Number of active flavors - sx : numpy.ndarray - List of harmonic sums: :math:`S_{1},S_{2}` + n : complex + Mellin moment + nf : int + Number of active flavors + sx : numpy.ndarray + List of harmonic sums: :math:`S_{1},S_{2}` Returns ------- - gamma_nsp : complex - |NLO| singlet-like non-singlet anomalous dimension - :math:`\\gamma_{ns,+}^{(1)}(N)` + gamma_nsp : complex + |NLO| singlet-like non-singlet anomalous dimension + :math:`\\gamma_{ns,+}^{(1)}(N)` """ S1 = sx[0] S2 = sx[1] @@ -98,23 +96,22 @@ def gamma_nsp(n, nf, sx): @nb.njit(cache=True) def gamma_ps(n, nf): - r""" - Compute the |NLO| pure-singlet quark-quark anomalous dimension. + r"""Compute the |NLO| pure-singlet quark-quark anomalous dimension. Implements Eq. (3.6) of :cite:`Vogt:2004mw`. Parameters ---------- - n : complex - Mellin moment - nf : int - Number of active flavors + n : complex + Mellin moment + nf : int + Number of active flavors Returns ------- - gamma_ps : complex - |NLO| pure-singlet quark-quark anomalous dimension - :math:`\\gamma_{ps}^{(1)}(N)` + gamma_ps : complex + |NLO| pure-singlet quark-quark anomalous dimension + :math:`\\gamma_{ps}^{(1)}(N)` """ # fmt: off gqqps1_nfcf = (-4*(2 + n*(5 + n))*(4 + n*(4 + n*(7 + 5*n))))/((-1 + n)*np.power(n,3)*np.power(1 + n,3)*np.power(2 + n,2)) # pylint: disable=line-too-long @@ -125,25 +122,24 @@ def gamma_ps(n, nf): @nb.njit(cache=True) def gamma_qg(n, nf, sx): - r""" - Compute the |NLO| quark-gluon singlet anomalous dimension. + r"""Compute the |NLO| quark-gluon singlet anomalous dimension. Implements Eq. (3.7) of :cite:`Vogt:2004mw`. Parameters ---------- - n : complex - Mellin moment - nf : int - Number of active flavors - sx : numpy.ndarray - List of harmonic sums: :math:`S_{1},S_{2}` + n : complex + Mellin moment + nf : int + Number of active flavors + sx : numpy.ndarray + List of harmonic sums: :math:`S_{1},S_{2}` Returns ------- - gamma_qg : complex - |NLO| quark-gluon singlet anomalous dimension - :math:`\\gamma_{qg}^{(1)}(N)` + gamma_qg : complex + |NLO| quark-gluon singlet anomalous dimension + :math:`\\gamma_{qg}^{(1)}(N)` """ S1 = sx[0] S2 = sx[1] @@ -160,25 +156,24 @@ def gamma_qg(n, nf, sx): @nb.njit(cache=True) def gamma_gq(n, nf, sx): - r""" - Compute the |NLO| gluon-quark singlet anomalous dimension. + r"""Compute the |NLO| gluon-quark singlet anomalous dimension. Implements Eq. (3.8) of :cite:`Vogt:2004mw`. Parameters ---------- - n : complex - Mellin moment - nf : int - Number of active flavors - sx : numpy.ndarray - List of harmonic sums: :math:`S_{1},S_{2}` + n : complex + Mellin moment + nf : int + Number of active flavors + sx : numpy.ndarray + List of harmonic sums: :math:`S_{1},S_{2}` Returns ------- - gamma_gq : complex - |NLO| gluon-quark singlet anomalous dimension - :math:`\\gamma_{gq}^{(1)}(N)` + gamma_gq : complex + |NLO| gluon-quark singlet anomalous dimension + :math:`\\gamma_{gq}^{(1)}(N)` """ S1 = sx[0] S2 = sx[1] @@ -198,25 +193,24 @@ def gamma_gq(n, nf, sx): @nb.njit(cache=True) def gamma_gg(n, nf, sx): - r""" - Compute the |NLO| gluon-gluon singlet anomalous dimension. + r"""Compute the |NLO| gluon-gluon singlet anomalous dimension. Implements Eq. (3.9) of :cite:`Vogt:2004mw`. Parameters ---------- - n : complex - Mellin moment - nf : int - Number of active flavors - sx : numpy.ndarray - List of harmonic sums: :math:`S_{1},S_{2}` + n : complex + Mellin moment + nf : int + Number of active flavors + sx : numpy.ndarray + List of harmonic sums: :math:`S_{1},S_{2}` Returns ------- - gamma_gg : complex - |NLO| gluon-gluon singlet anomalous dimension - :math:`\\gamma_{gg}^{(1)}(N)` + gamma_gg : complex + |NLO| gluon-gluon singlet anomalous dimension + :math:`\\gamma_{gg}^{(1)}(N)` """ S1 = sx[0] Sp1p = harmonics.S1(n / 2) @@ -236,14 +230,13 @@ def gamma_gg(n, nf, sx): @nb.njit(cache=True) def gamma_singlet(n, nf, sx): - r""" - Compute the next-leading-order singlet anomalous dimension matrix. + r"""Compute the next-leading-order singlet anomalous dimension matrix. .. math:: - \gamma_S^{(1)} = \left(\begin{array}{cc} - \gamma_{qq}^{(1)} & \gamma_{qg}^{(1)}\\ - \gamma_{gq}^{(1)} & \gamma_{gg}^{(1)} - \end{array}\right) + \\gamma_S^{(1)} = \\left(\begin{array}{cc} + \\gamma_{qq}^{(1)} & \\gamma_{qg}^{(1)}\\ + \\gamma_{gq}^{(1)} & \\gamma_{gg}^{(1)} + \\end{array}\right) Parameters ---------- @@ -257,15 +250,15 @@ def gamma_singlet(n, nf, sx): Returns ------- gamma_S_1 : numpy.ndarray - |NLO| singlet anomalous dimension matrix :math:`\gamma_{S}^{(1)}(N)` + |NLO| singlet anomalous dimension matrix :math:`\\gamma_{S}^{(1)}(N)` See Also -------- - gamma_nsp : :math:`\gamma_{qq}^{(1)}` - gamma_ps : :math:`\gamma_{qq}^{(1)}` - gamma_qg : :math:`\gamma_{qg}^{(1)}` - gamma_gq : :math:`\gamma_{gq}^{(1)}` - gamma_gg : :math:`\gamma_{gg}^{(1)}` + gamma_nsp : :math:`\\gamma_{qq}^{(1)}` + gamma_ps : :math:`\\gamma_{qq}^{(1)}` + gamma_qg : :math:`\\gamma_{qg}^{(1)}` + gamma_gq : :math:`\\gamma_{gq}^{(1)}` + gamma_gg : :math:`\\gamma_{gg}^{(1)}` """ gamma_qq = gamma_nsp(n, nf, sx) + gamma_ps(n, nf) gamma_S_0 = np.array( @@ -277,16 +270,15 @@ def gamma_singlet(n, nf, sx): @nb.njit(cache=True) def gamma_singlet_qed(N, nf, sx): - r""" - Compute the leading-order singlet anomalous dimension matrix. + r"""Compute the leading-order singlet anomalous dimension matrix. .. math:: - \gamma_S^{(2,0)} = \left(\begin{array}{cccc} - \gamma_{gg}^{(2,0)} & 0 & \gamma_{gq}^{(2,0)} & 0\\ + \\gamma_S^{(2,0)} = \\left(\begin{array}{cccc} + \\gamma_{gg}^{(2,0)} & 0 & \\gamma_{gq}^{(2,0)} & 0\\ 0 & 0 & 0 & 0 \\ - \gamma_{qg}^{(2,0)} & 0 & \gamma_{qq}^{(2,0)} & 0 \\ - 0 & 0 & 0 & \gamma_{qq}^{(2,0)} \\ - \end{array}\right) + \\gamma_{qg}^{(2,0)} & 0 & \\gamma_{qq}^{(2,0)} & 0 \\ + 0 & 0 & 0 & \\gamma_{qq}^{(2,0)} \\ + \\end{array}\right) Parameters ---------- @@ -300,14 +292,14 @@ def gamma_singlet_qed(N, nf, sx): Returns ------- gamma_S_0 : numpy.ndarray - Leading-order singlet anomalous dimension matrix :math:`\gamma_{S}^{(0)}(N)` + Leading-order singlet anomalous dimension matrix :math:`\\gamma_{S}^{(0)}(N)` See Also -------- - gamma_ns : :math:`\gamma_{qq}^{(0)}` - gamma_qg : :math:`\gamma_{qg}^{(0)}` - gamma_gq : :math:`\gamma_{gq}^{(0)}` - gamma_gg : :math:`\gamma_{gg}^{(0)}` + gamma_ns : :math:`\\gamma_{qq}^{(0)}` + gamma_qg : :math:`\\gamma_{qg}^{(0)}` + gamma_gq : :math:`\\gamma_{gq}^{(0)}` + gamma_gg : :math:`\\gamma_{gg}^{(0)}` """ gamma_ns_p = gamma_nsp(N, nf, sx) gamma_qq = gamma_ns_p + gamma_ps(N, nf) @@ -325,14 +317,13 @@ def gamma_singlet_qed(N, nf, sx): @nb.njit(cache=True) def gamma_valence_qed(N, nf, sx): - r""" - Compute the leading-order valence anomalous dimension matrix. + r"""Compute the leading-order valence anomalous dimension matrix. .. math:: - \gamma_V^{(2,0)} = \left(\begin{array}{cc} - \gamma_{ns-}^{(2,0)} & 0\\ - 0 & \gamma_{ns-}^{(2,0)} - \end{array}\right) + \\gamma_V^{(2,0)} = \\left(\begin{array}{cc} + \\gamma_{ns-}^{(2,0)} & 0\\ + 0 & \\gamma_{ns-}^{(2,0)} + \\end{array}\right) Parameters ---------- @@ -346,14 +337,14 @@ def gamma_valence_qed(N, nf, sx): Returns ------- gamma_S_0 : numpy.ndarray - Leading-order singlet anomalous dimension matrix :math:`\gamma_{S}^{(0)}(N)` + Leading-order singlet anomalous dimension matrix :math:`\\gamma_{S}^{(0)}(N)` See Also -------- - gamma_ns : :math:`\gamma_{qq}^{(0)}` - gamma_qg : :math:`\gamma_{qg}^{(0)}` - gamma_gq : :math:`\gamma_{gq}^{(0)}` - gamma_gg : :math:`\gamma_{gg}^{(0)}` + gamma_ns : :math:`\\gamma_{qq}^{(0)}` + gamma_qg : :math:`\\gamma_{qg}^{(0)}` + gamma_gq : :math:`\\gamma_{gq}^{(0)}` + gamma_gg : :math:`\\gamma_{gg}^{(0)}` """ gamma_V = np.array( [ diff --git a/src/eko/anomalous_dimensions/as3.py b/src/eko/anomalous_dimensions/as3.py index 094f858c6..ac1dda445 100644 --- a/src/eko/anomalous_dimensions/as3.py +++ b/src/eko/anomalous_dimensions/as3.py @@ -13,25 +13,24 @@ @nb.njit(cache=True) def gamma_nsm(n, nf, sx): - r""" - Compute the |NNLO| valence-like non-singlet anomalous dimension. + r"""Compute the |NNLO| valence-like non-singlet anomalous dimension. Implements Eq. (3.8) of :cite:`Moch:2004pa`. Parameters ---------- - n : complex - Mellin moment - nf : int - Number of active flavors - sx : np.ndarray - List of harmonic sums: :math:`S_{1},S_{2},S_{3}` + n : complex + Mellin moment + nf : int + Number of active flavors + sx : np.ndarray + List of harmonic sums: :math:`S_{1},S_{2},S_{3}` Returns ------- - gamma_nsm : complex - |NNLO| valence-like non-singlet anomalous dimension - :math:`\\gamma_{ns,-}^{(2)}(N)` + gamma_nsm : complex + |NNLO| valence-like non-singlet anomalous dimension + :math:`\\gamma_{ns,-}^{(2)}(N)` """ S1 = sx[0] S2 = sx[1] @@ -90,25 +89,24 @@ def gamma_nsm(n, nf, sx): @nb.njit(cache=True) def gamma_nsp(n, nf, sx): - r""" - Compute the |NNLO| singlet-like non-singlet anomalous dimension. + r"""Compute the |NNLO| singlet-like non-singlet anomalous dimension. Implements Eq. (3.7) of :cite:`Moch:2004pa`. Parameters ---------- - n : complex - Mellin moment - nf : int - Number of active flavors - sx : np.ndarray - List of harmonic sums: :math:`S_{1},S_{2},S_{3}` + n : complex + Mellin moment + nf : int + Number of active flavors + sx : np.ndarray + List of harmonic sums: :math:`S_{1},S_{2},S_{3}` Returns ------- - gamma_nsp : complex - |NNLO| singlet-like non-singlet anomalous dimension - :math:`\\gamma_{ns,+}^{(2)}(N)` + gamma_nsp : complex + |NNLO| singlet-like non-singlet anomalous dimension + :math:`\\gamma_{ns,+}^{(2)}(N)` """ S1 = sx[0] S2 = sx[1] @@ -167,25 +165,24 @@ def gamma_nsp(n, nf, sx): @nb.njit(cache=True) def gamma_nsv(n, nf, sx): - r""" - Compute the |NNLO| valence non-singlet anomalous dimension. + r"""Compute the |NNLO| valence non-singlet anomalous dimension. Implements Eq. (3.9) of :cite:`Moch:2004pa`. Parameters ---------- - n : complex - Mellin moment - nf : int - Number of active flavors - sx : np.ndarray - List of harmonic sums: :math:`S_{1},S_{2},S_{3}` + n : complex + Mellin moment + nf : int + Number of active flavors + sx : np.ndarray + List of harmonic sums: :math:`S_{1},S_{2},S_{3}` Returns ------- - gamma_nsv : complex - |NNLO| valence non-singlet anomalous dimension - :math:`\\gamma_{ns,v}^{(2)}(N)` + gamma_nsv : complex + |NNLO| valence non-singlet anomalous dimension + :math:`\\gamma_{ns,v}^{(2)}(N)` """ S1 = sx[0] S2 = sx[1] @@ -222,25 +219,24 @@ def gamma_nsv(n, nf, sx): @nb.njit(cache=True) def gamma_ps(n, nf, sx): - r""" - Compute the |NNLO| pure-singlet quark-quark anomalous dimension. + r"""Compute the |NNLO| pure-singlet quark-quark anomalous dimension. Implements Eq. (3.10) of :cite:`Vogt:2004mw`. Parameters ---------- - n : complex - Mellin moment - nf : int - Number of active flavors - sx : np.ndarray - List of harmonic sums: :math:`S_{1},S_{2},S_{3}` + n : complex + Mellin moment + nf : int + Number of active flavors + sx : np.ndarray + List of harmonic sums: :math:`S_{1},S_{2},S_{3}` Returns ------- - gamma_ps : complex - |NNLO| pure-singlet quark-quark anomalous dimension - :math:`\\gamma_{ps}^{(2)}(N)` + gamma_ps : complex + |NNLO| pure-singlet quark-quark anomalous dimension + :math:`\\gamma_{ps}^{(2)}(N)` """ S1 = sx[0] S2 = sx[1] @@ -294,25 +290,24 @@ def gamma_ps(n, nf, sx): @nb.njit(cache=True) def gamma_qg(n, nf, sx): - r""" - Compute the |NNLO| quark-gluon singlet anomalous dimension. + r"""Compute the |NNLO| quark-gluon singlet anomalous dimension. Implements Eq. (3.11) of :cite:`Vogt:2004mw`. Parameters ---------- - n : complex - Mellin moment - nf : int - Number of active flavors - sx : np.ndarray - List of harmonic sums: :math:`S_{1},S_{2},S_{3}` + n : complex + Mellin moment + nf : int + Number of active flavors + sx : np.ndarray + List of harmonic sums: :math:`S_{1},S_{2},S_{3}` Returns ------- - gamma_qg : complex - |NNLO| quark-gluon singlet anomalous dimension - :math:`\\gamma_{qg}^{(2)}(N)` + gamma_qg : complex + |NNLO| quark-gluon singlet anomalous dimension + :math:`\\gamma_{qg}^{(2)}(N)` """ S1 = sx[0] S2 = sx[1] @@ -368,25 +363,24 @@ def gamma_qg(n, nf, sx): @nb.njit(cache=True) def gamma_gq(n, nf, sx): - r""" - Compute the |NNLO| gluon-quark singlet anomalous dimension. + r"""Compute the |NNLO| gluon-quark singlet anomalous dimension. Implements Eq. (3.12) of :cite:`Vogt:2004mw`. Parameters ---------- - n : complex - Mellin moment - nf : int - Number of active flavors - sx : np.ndarray - List of harmonic sums: :math:`S_{1},S_{2},S_{3}` + n : complex + Mellin moment + nf : int + Number of active flavors + sx : np.ndarray + List of harmonic sums: :math:`S_{1},S_{2},S_{3}` Returns ------- - gamma_gq : complex - |NNLO| gluon-quark singlet anomalous dimension - :math:`\\gamma_{gq}^{(2)}(N)` + gamma_gq : complex + |NNLO| gluon-quark singlet anomalous dimension + :math:`\\gamma_{gq}^{(2)}(N)` """ S1 = sx[0] S2 = sx[1] @@ -458,25 +452,24 @@ def gamma_gq(n, nf, sx): @nb.njit(cache=True) def gamma_gg(n, nf, sx): - r""" - Compute the |NNLO| gluon-gluon singlet anomalous dimension. + r"""Compute the |NNLO| gluon-gluon singlet anomalous dimension. Implements Eq. (3.13) of :cite:`Vogt:2004mw`. Parameters ---------- - n : complex - Mellin moment - nf : int - Number of active flavors - sx : np.ndarray - List of harmonic sums: :math:`S_{1},S_{2},S_{3}` + n : complex + Mellin moment + nf : int + Number of active flavors + sx : np.ndarray + List of harmonic sums: :math:`S_{1},S_{2},S_{3}` Returns ------- - gamma_gg : complex - |NNLO| gluon-gluon singlet anomalous dimension - :math:`\\gamma_{gg}^{(2)}(N)` + gamma_gg : complex + |NNLO| gluon-gluon singlet anomalous dimension + :math:`\\gamma_{gg}^{(2)}(N)` """ S1 = sx[0] S2 = sx[1] @@ -546,14 +539,13 @@ def gamma_gg(n, nf, sx): @nb.njit(cache=True) def gamma_singlet(N, nf, sx): - r""" - Compute the |NNLO| singlet anomalous dimension matrix. + r"""Compute the |NNLO| singlet anomalous dimension matrix. .. math:: - \gamma_S^{(2)} = \left(\begin{array}{cc} - \gamma_{qq}^{(2)} & \gamma_{qg}^{(2)}\\ - \gamma_{gq}^{(2)} & \gamma_{gg}^{(2)} - \end{array}\right) + \\gamma_S^{(2)} = \\left(\begin{array}{cc} + \\gamma_{qq}^{(2)} & \\gamma_{qg}^{(2)}\\ + \\gamma_{gq}^{(2)} & \\gamma_{gg}^{(2)} + \\end{array}\right) Parameters ---------- @@ -569,15 +561,15 @@ def gamma_singlet(N, nf, sx): ------- gamma_S_2 : numpy.ndarray |NNLO| singlet anomalous dimension matrix - :math:`\gamma_{S}^{(2)}(N)` + :math:`\\gamma_{S}^{(2)}(N)` See Also -------- - gamma_nsp : :math:`\gamma_{ns,+}^{(2)}` - gamma_ps : :math:`\gamma_{ps}^{(2)}` - gamma_qg : :math:`\gamma_{qg}^{(2)}` - gamma_gq : :math:`\gamma_{gq}^{(2)}` - gamma_gg : :math:`\gamma_{gg}^{(2)}` + gamma_nsp : :math:`\\gamma_{ns,+}^{(2)}` + gamma_ps : :math:`\\gamma_{ps}^{(2)}` + gamma_qg : :math:`\\gamma_{qg}^{(2)}` + gamma_gq : :math:`\\gamma_{gq}^{(2)}` + gamma_gg : :math:`\\gamma_{gg}^{(2)}` """ gamma_qq = gamma_nsp(N, nf, sx) + gamma_ps(N, nf, sx) gamma_S_0 = np.array( @@ -589,16 +581,15 @@ def gamma_singlet(N, nf, sx): @nb.njit(cache=True) def gamma_singlet_qed(N, nf, sx): - r""" - Compute the leading-order singlet anomalous dimension matrix. + r"""Compute the leading-order singlet anomalous dimension matrix. .. math:: - \gamma_S^{(3,0)} = \left(\begin{array}{cccc} - \gamma_{gg}^{(3,0)} & 0 & \gamma_{gq}^{(3,0)} & 0\\ + \\gamma_S^{(3,0)} = \\left(\begin{array}{cccc} + \\gamma_{gg}^{(3,0)} & 0 & \\gamma_{gq}^{(3,0)} & 0\\ 0 & 0 & 0 & 0 \\ - \gamma_{qg}^{(3,0)} & 0 & \gamma_{qq}^{(3,0)} & 0 \\ - 0 & 0 & 0 & \gamma_{qq}^{(3,0)} \\ - \end{array}\right) + \\gamma_{qg}^{(3,0)} & 0 & \\gamma_{qq}^{(3,0)} & 0 \\ + 0 & 0 & 0 & \\gamma_{qq}^{(3,0)} \\ + \\end{array}\right) Parameters ---------- @@ -612,14 +603,14 @@ def gamma_singlet_qed(N, nf, sx): Returns ------- gamma_S_0 : numpy.ndarray - Leading-order singlet anomalous dimension matrix :math:`\gamma_{S}^{(0)}(N)` + Leading-order singlet anomalous dimension matrix :math:`\\gamma_{S}^{(0)}(N)` See Also -------- - gamma_ns : :math:`\gamma_{qq}^{(0)}` - gamma_qg : :math:`\gamma_{qg}^{(0)}` - gamma_gq : :math:`\gamma_{gq}^{(0)}` - gamma_gg : :math:`\gamma_{gg}^{(0)}` + gamma_ns : :math:`\\gamma_{qq}^{(0)}` + gamma_qg : :math:`\\gamma_{qg}^{(0)}` + gamma_gq : :math:`\\gamma_{gq}^{(0)}` + gamma_gg : :math:`\\gamma_{gg}^{(0)}` """ gamma_np_p = gamma_nsp(N, nf, sx) gamma_qq = gamma_np_p + gamma_ps(N, nf, sx) @@ -637,14 +628,13 @@ def gamma_singlet_qed(N, nf, sx): @nb.njit(cache=True) def gamma_valence_qed(N, nf, sx): - r""" - Compute the leading-order valence anomalous dimension matrix. + r"""Compute the leading-order valence anomalous dimension matrix. .. math:: - \gamma_V^{(3,0)} = \left(\begin{array}{cc} - \gamma_{nsV}^{(3,0)} & 0\\ - 0 & \gamma_{ns-}^{(3,0)} - \end{array}\right) + \\gamma_V^{(3,0)} = \\left(\begin{array}{cc} + \\gamma_{nsV}^{(3,0)} & 0\\ + 0 & \\gamma_{ns-}^{(3,0)} + \\end{array}\right) Parameters ---------- @@ -658,14 +648,14 @@ def gamma_valence_qed(N, nf, sx): Returns ------- gamma_S_0 : numpy.ndarray - Leading-order singlet anomalous dimension matrix :math:`\gamma_{S}^{(0)}(N)` + Leading-order singlet anomalous dimension matrix :math:`\\gamma_{S}^{(0)}(N)` See Also -------- - gamma_ns : :math:`\gamma_{qq}^{(0)}` - gamma_qg : :math:`\gamma_{qg}^{(0)}` - gamma_gq : :math:`\gamma_{gq}^{(0)}` - gamma_gg : :math:`\gamma_{gg}^{(0)}` + gamma_ns : :math:`\\gamma_{qq}^{(0)}` + gamma_qg : :math:`\\gamma_{qg}^{(0)}` + gamma_gq : :math:`\\gamma_{gq}^{(0)}` + gamma_gg : :math:`\\gamma_{gg}^{(0)}` """ gamma_V = np.array( [ From edd9fc1fa66d5d9f3db692a029dbeedfe1053a45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 1 Dec 2022 12:32:56 +0100 Subject: [PATCH 252/312] Enforce numpy docstring stile in evolution operator --- src/eko/evolution_operator/__init__.py | 35 +++++++++++++------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 1ef5e2918..0a4984c0f 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -53,21 +53,20 @@ def select_singlet_element(ker, mode0, mode1): @nb.njit(cache=True) def select_QEDsinglet_element(ker, mode0, mode1): - """ - Select element of the QEDsinglet matrix. + """Select element of the QEDsinglet matrix. Parameters ---------- - ker : numpy.ndarray - QEDsinglet integration kernel - mode0 : int - id for first sector element - mode1 : int - id for second sector element + ker : numpy.ndarray + QEDsinglet integration kernel + mode0 : int + id for first sector element + mode1 : int + id for second sector element Returns ------- - ker : complex - QEDsinglet integration kernel element + ker : complex + QEDsinglet integration kernel element """ if mode0 == 21: index1 = 0 @@ -95,16 +94,16 @@ def select_QEDvalence_element(ker, mode0, mode1): Parameters ---------- - ker : numpy.ndarray - QEDvalence integration kernel - mode0 : int - id for first sector element - mode1 : int - id for second sector element + ker : numpy.ndarray + QEDvalence integration kernel + mode0 : int + id for first sector element + mode1 : int + id for second sector element Returns ------- - ker : complex - QEDvalence integration kernel element + ker : complex + QEDvalence integration kernel element """ index1 = 0 if mode0 == 10200 else 1 index2 = 0 if mode1 == 10200 else 1 From 33569deac6095373a8ff654b1c0f97c4bfd5579d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 1 Dec 2022 12:42:48 +0100 Subject: [PATCH 253/312] Enforce numpy docstring stile in kernels --- src/eko/evolution_operator/flavors.py | 101 ++++---- src/eko/kernels/evolution_integrals_qed.py | 199 ++++++++-------- src/eko/kernels/non_singlet_qed.py | 263 ++++++++++----------- src/eko/kernels/singlet_qed.py | 75 +++--- 4 files changed, 307 insertions(+), 331 deletions(-) diff --git a/src/eko/evolution_operator/flavors.py b/src/eko/evolution_operator/flavors.py index 9cce6deb2..29fddc422 100644 --- a/src/eko/evolution_operator/flavors.py +++ b/src/eko/evolution_operator/flavors.py @@ -7,8 +7,7 @@ def pids_from_intrinsic_evol(label, nf, normalize): - r""" - Obtain the list of pids with their corresponding weight, that are contributing to ``evol``. + r"""Obtain the list of pids with their corresponding weight, that are contributing to ``evol``. The normalization of the weights is only needed for the output rotation: @@ -22,16 +21,16 @@ def pids_from_intrinsic_evol(label, nf, normalize): Parameters ---------- - evol : str - evolution label - nf : int - maximum number of light flavors - normalize : bool - normalize output + evol : str + evolution label + nf : int + maximum number of light flavors + normalize : bool + normalize output Returns ------- - m : list + m : list """ try: evol_idx = br.evol_basis.index(label) @@ -53,8 +52,7 @@ def pids_from_intrinsic_evol(label, nf, normalize): def get_range(evol_labels, qed=False): - """ - Determine the number of light and heavy flavors participating in the input and output. + """Determine the number of light and heavy flavors participating in the input and output. Here, we assume that the T distributions (e.g. T15) appears *always* before the corresponding V distribution (e.g. V15). @@ -66,10 +64,10 @@ def get_range(evol_labels, qed=False): Returns ------- - nf_in : int - number of light flavors in the input - nf_out : int - number of light flavors in the output + nf_in : int + number of light flavors in the input + nf_out : int + number of light flavors in the output """ nf_in = 3 nf_out = 3 @@ -100,18 +98,17 @@ def update(label, qed=False): def rotate_pm_to_flavor(label): - """ - Rotate from +- basis to flavor basis. + """Rotate from +- basis to flavor basis. Parameters ---------- - label : str - label + label : str + label Returns ------- - l : list(float) - list of weights + l : list(float) + list of weights """ # g and ph are unaltered if label in ["g", "ph"]: @@ -132,22 +129,21 @@ def rotate_pm_to_flavor(label): def rotate_matching(nf, qed=False, inverse=False): - """ - Rotation between matching basis (with e.g. S,g,...V8 and c+,c-) and new true evolution basis (with S,g,...V8,T15,V15). + """Rotation between matching basis (with e.g. S,g,...V8 and c+,c-) and new true evolution basis (with S,g,...V8,T15,V15). Parameters ---------- - nf : int - number of active flavors in the higher patch: to activate T15, nf=4 - qed : bool - use qed? - inverse : bool - use inverse conditions? + nf : int + number of active flavors in the higher patch: to activate T15, nf=4 + qed : bool + use qed? + inverse : bool + use inverse conditions? Returns ------- - l : dict - mapping in dot notation between the bases + l : dict + mapping in dot notation between the bases """ # the gluon and the photon do not care about new quarks l = {"g.g": 1.0, "ph.ph": 1.0} @@ -210,37 +206,35 @@ def rotate_matching(nf, qed=False, inverse=False): def rotate_matching_inverse(nf, qed=False): - """ - Inverse rotation between matching basis (with e.g. S,g,...V8 and c+,c-) and new true evolution basis (with S,g,...V8,T15,V15). + """Inverse rotation between matching basis (with e.g. S,g,...V8 and c+,c-) and new true evolution basis (with S,g,...V8,T15,V15). Parameters ---------- - nf : int - number of active flavors in the higher patch: to activate T15, nf=4 - qed : bool - use qed? + nf : int + number of active flavors in the higher patch: to activate T15, nf=4 + qed : bool + use qed? Returns ------- - l : dict - mapping in dot notation between the bases + l : dict + mapping in dot notation between the bases """ return rotate_matching(nf, qed, True) def rotation_parameters(nf): - """ - Parameters of the basis rotation from (S, Sdelta, h+) into (S, Sdelta, T_i), or equivalentely for V, Vdelta, V_i, h-. + """Parameters of the basis rotation from (S, Sdelta, h+) into (S, Sdelta, T_i), or equivalentely for V, Vdelta, V_i, h-. Parameters ---------- - nf : int - number of active flavors in the higher patch: to activate T3u V3u nf=4 + nf : int + number of active flavors in the higher patch: to activate T3u V3u nf=4 Returns ------- - a,b,c,d,e,f : float - Parameters of the rotation: Sdelta = a*S + b*Sdelta + c*h+, T_i = d*S + e*Sdelta + f*h+ + a,b,c,d,e,f : float + Parameters of the rotation: Sdelta = a*S + b*Sdelta + c*h+, T_i = d*S + e*Sdelta + f*h+ """ nu_l = constants.uplike_flavors(nf - 1) nd_l = (nf - 1) - nu_l @@ -266,21 +260,20 @@ def rotation_parameters(nf): def pids_from_intrinsic_unified_evol(label, nf, normalize): - r""" - Obtain the list of pids with their corresponding weight, that are contributing to intrinsic unified evolution. + r"""Obtain the list of pids with their corresponding weight, that are contributing to intrinsic unified evolution. Parameters ---------- - evol : str - evolution label - nf : int - maximum number of light flavors - normalize : bool - normalize output + evol : str + evolution label + nf : int + maximum number of light flavors + normalize : bool + normalize output Returns ------- - m : list + m : list """ if label in ["ph", "g", "S", "V"]: return pids_from_intrinsic_evol(label, nf, normalize) diff --git a/src/eko/kernels/evolution_integrals_qed.py b/src/eko/kernels/evolution_integrals_qed.py index 077054e1a..a70d2fc8b 100644 --- a/src/eko/kernels/evolution_integrals_qed.py +++ b/src/eko/kernels/evolution_integrals_qed.py @@ -8,8 +8,7 @@ @nb.njit(cache=True) def j00_qed(a1, a0, aem, nf): - r""" - LO-LO QED exact evolution integral. + r"""LO-LO QED exact evolution integral. .. math:: j^{(0,0)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{(\beta_0 + aem \beta_{0,1}) a_s'} @@ -17,19 +16,19 @@ def j00_qed(a1, a0, aem, nf): Parameters ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors + a1 : float + target coupling value + a0 : float + initial coupling value + aem : float + electromagnetic coupling value + nf : int + number of active flavors Returns ------- - j00 : float - integral + j00 : float + integral """ beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) return np.log(a1 / a0) / beta0 @@ -37,8 +36,7 @@ def j00_qed(a1, a0, aem, nf): @nb.njit(cache=True) def jm10(a1, a0, aem, nf): - r""" - LO-LO QED exact evolution integral. + r"""LO-LO QED exact evolution integral. .. math:: j^{(-1,0)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{(\beta_0 + aem \beta_{0,1}) a_s'^2} @@ -46,19 +44,19 @@ def jm10(a1, a0, aem, nf): Parameters ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors + a1 : float + target coupling value + a0 : float + initial coupling value + aem : float + electromagnetic coupling value + nf : int + number of active flavors Returns ------- - j00 : float - integral + j00 : float + integral """ beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) return (1.0 / a0 - 1.0 / a1) / beta0 @@ -66,8 +64,7 @@ def jm10(a1, a0, aem, nf): @nb.njit(cache=True) def j11_exact_qed(a1, a0, aem, nf): - r""" - NLO-NLO exact evolution integral. + r"""NLO-NLO exact evolution integral. .. math:: j^{(1,1)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s}\!da_s'\, @@ -76,19 +73,19 @@ def j11_exact_qed(a1, a0, aem, nf): Parameters ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors + a1 : float + target coupling value + a0 : float + initial coupling value + aem : float + electromagnetic coupling value + nf : int + number of active flavors Returns ------- - j11 : float - integral + j11 : float + integral """ beta1 = beta.beta_qcd((3, 0), nf) beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) @@ -98,8 +95,7 @@ def j11_exact_qed(a1, a0, aem, nf): @nb.njit(cache=True) def j01_exact_qed(a1, a0, aem, nf): - r""" - LO-NLO QED exact evolution integral. + r"""LO-NLO QED exact evolution integral. .. math:: j^{(0,1)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s}\!da_s'\, @@ -108,19 +104,19 @@ def j01_exact_qed(a1, a0, aem, nf): Parameters ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors + a1 : float + target coupling value + a0 : float + initial coupling value + aem : float + electromagnetic coupling value + nf : int + number of active flavors Returns ------- - j11 : float - integral + j11 : float + integral """ beta1 = beta.beta_qcd((3, 0), nf) beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) @@ -130,8 +126,7 @@ def j01_exact_qed(a1, a0, aem, nf): @nb.njit(cache=True) def jm11_exact(a1, a0, aem, nf): - r""" - LO-NLO exact evolution integral. + r"""LO-NLO exact evolution integral. .. math:: j^{(-1,1)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3} @@ -139,17 +134,17 @@ def jm11_exact(a1, a0, aem, nf): Parameters ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - nf : int - number of active flavors + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors Returns ------- - j11 : float - integral + j11 : float + integral """ beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) b1 = beta.beta_qcd((3, 0), nf) / beta0 @@ -160,8 +155,7 @@ def jm11_exact(a1, a0, aem, nf): @nb.njit(cache=True) def j22_exact_qed(a1, a0, aem, nf): - r""" - NNLO-NNLO exact evolution integral. + r"""NNLO-NNLO exact evolution integral. .. math:: j^{(2,2)}(a_s,a_s^0,aem) &= @@ -176,19 +170,19 @@ def j22_exact_qed(a1, a0, aem, nf): Parameters ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors + a1 : float + target coupling value + a0 : float + initial coupling value + aem : float + electromagnetic coupling value + nf : int + number of active flavors Returns ------- - j22 : complex - integral + j22 : complex + integral """ beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) b1 = beta.beta_qcd((3, 0), nf) / beta0 @@ -206,8 +200,7 @@ def j22_exact_qed(a1, a0, aem, nf): @nb.njit(cache=True) def j12_exact_qed(a1, a0, aem, nf): - r""" - NLO-NNLO exact evolution integral. + r"""NLO-NNLO exact evolution integral. .. math:: j^{(1,2)}(a_s,a_s^0,aem) &= \int\limits_{a_s^0}^{a_s}\!da_s'\,\frac{a_s'^2}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ @@ -217,19 +210,19 @@ def j12_exact_qed(a1, a0, aem, nf): Parameters ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors + a1 : float + target coupling value + a0 : float + initial coupling value + aem : float + electromagnetic coupling value + nf : int + number of active flavors Returns ------- - j12 : complex - integral + j12 : complex + integral """ # pylint: disable=line-too-long beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) b1 = beta.beta_qcd((3, 0), nf) / beta0 @@ -244,8 +237,7 @@ def j12_exact_qed(a1, a0, aem, nf): @nb.njit(cache=True) def j02_exact_qed(a1, a0, aem, nf): - r""" - LO-NNLO exact evolution integral. + r"""LO-NNLO exact evolution integral. .. math:: j^{(0,2)}(a_s,a_s^0,aem) &= \int\limits_{a_s^0}^{a_s}\!da_s'\, @@ -254,19 +246,19 @@ def j02_exact_qed(a1, a0, aem, nf): Parameters ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors + a1 : float + target coupling value + a0 : float + initial coupling value + aem : float + electromagnetic coupling value + nf : int + number of active flavors Returns ------- - j02 : complex - integral + j02 : complex + integral """ beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) b1 = beta.beta_qcd((3, 0), nf) / beta0 @@ -280,8 +272,7 @@ def j02_exact_qed(a1, a0, aem, nf): @nb.njit(cache=True) def jm12_exact(a1, a0, aem, nf): - r""" - LO-NNLO exact evolution integral. + r"""LO-NNLO exact evolution integral. .. math:: j^{(-1,2)}(a_s,a_s^0,aem) &= \int\limits_{a_s^0}^{a_s}\!da_s'\, @@ -290,17 +281,17 @@ def jm12_exact(a1, a0, aem, nf): Parameters ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - nf : int - number of active flavors + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors Returns ------- - j02 : complex - integral + j02 : complex + integral """ beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) b1 = beta.beta_qcd((3, 0), nf) / beta0 diff --git a/src/eko/kernels/non_singlet_qed.py b/src/eko/kernels/non_singlet_qed.py index 28ea4d6e0..d10ee6d56 100644 --- a/src/eko/kernels/non_singlet_qed.py +++ b/src/eko/kernels/non_singlet_qed.py @@ -8,26 +8,25 @@ @nb.njit(cache=True) def as1aem1_exact(gamma_ns, a1, a0, aem, nf): - """ - O(as1aem1) non-singlet exact EKO. + """O(as1aem1) non-singlet exact EKO. Parameters ---------- - gamma_ns : numpy.ndarray - non-singlet anomalous dimensions - a1 : float - target coupling value - a0 : float - initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors + gamma_ns : numpy.ndarray + non-singlet anomalous dimensions + a1 : float + target coupling value + a0 : float + initial coupling value + aem : float + electromagnetic coupling value + nf : int + number of active flavors Returns ------- - e_ns^0 : complex - O(as1aem1) non-singlet exact EKO + e_ns^0 : complex + O(as1aem1) non-singlet exact EKO """ return np.exp( (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei_qed.j00_qed(a1, a0, aem, nf) @@ -42,21 +41,21 @@ def as1aem2_exact(gamma_ns, a1, a0, aem, nf): Parameters ---------- - gamma_ns : numpy.ndarray - non-singlet anomalous dimensions - a1 : float - target coupling value - a0 : float - initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors + gamma_ns : numpy.ndarray + non-singlet anomalous dimensions + a1 : float + target coupling value + a0 : float + initial coupling value + aem : float + electromagnetic coupling value + nf : int + number of active flavors Returns ------- - e_ns^0 : complex - O(as1aem2) non-singlet exact EKO + e_ns^0 : complex + O(as1aem2) non-singlet exact EKO """ return np.exp( (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei_qed.j00_qed(a1, a0, aem, nf) @@ -67,26 +66,25 @@ def as1aem2_exact(gamma_ns, a1, a0, aem, nf): @nb.njit(cache=True) def as2aem1_exact(gamma_ns, a1, a0, aem, nf): - """ - O(as2aem1) non-singlet exact EKO. + """O(as2aem1) non-singlet exact EKO. Parameters ---------- - gamma_ns : numpy.ndarray - non-singlet anomalous dimensions - a1 : float - target coupling value - a0 : float - initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors + gamma_ns : numpy.ndarray + non-singlet anomalous dimensions + a1 : float + target coupling value + a0 : float + initial coupling value + aem : float + electromagnetic coupling value + nf : int + number of active flavors Returns ------- - e_ns^1 : complex - O(as2aem1) non-singlet exact EKO + e_ns^1 : complex + O(as2aem1) non-singlet exact EKO """ return np.exp( (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei_qed.j01_exact_qed(a1, a0, aem, nf) @@ -97,26 +95,25 @@ def as2aem1_exact(gamma_ns, a1, a0, aem, nf): @nb.njit(cache=True) def as2aem2_exact(gamma_ns, a1, a0, aem, nf): - """ - O(as2aem2) non-singlet exact EKO. + """O(as2aem2) non-singlet exact EKO. Parameters ---------- - gamma_ns : numpy.ndarray - non-singlet anomalous dimensions - a1 : float - target coupling value - a0 : float - initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors + gamma_ns : numpy.ndarray + non-singlet anomalous dimensions + a1 : float + target coupling value + a0 : float + initial coupling value + aem : float + electromagnetic coupling value + nf : int + number of active flavors Returns ------- - e_ns^1 : complex - O(as2aem2) non-singlet exact EKO + e_ns^1 : complex + O(as2aem2) non-singlet exact EKO """ return np.exp( (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei_qed.j01_exact_qed(a1, a0, aem, nf) @@ -128,26 +125,25 @@ def as2aem2_exact(gamma_ns, a1, a0, aem, nf): @nb.njit(cache=True) def as3aem1_exact(gamma_ns, a1, a0, aem, nf): - """ - O(as3aem1) non-singlet exact EKO. + """O(as3aem1) non-singlet exact EKO. Parameters ---------- - gamma_ns : numpy.ndarray - non-singlet anomalous dimensions - a1 : float - target coupling value - a0 : float - initial coupling value - aem : float + gamma_ns : numpy.ndarray + non-singlet anomalous dimensions + a1 : float + target coupling value + a0 : float + initial coupling value + aem : float electromagnetic coupling value - nf : int - number of active flavors + nf : int + number of active flavors Returns ------- - e_ns^2 : complex - O(as3aem1) non-singlet exact EKO + e_ns^2 : complex + O(as3aem1) non-singlet exact EKO """ return np.exp( (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei_qed.j02_exact_qed(a1, a0, aem, nf) @@ -164,21 +160,21 @@ def as3aem2_exact(gamma_ns, a1, a0, aem, nf): Parameters ---------- - gamma_ns : numpy.ndarray - non-singlet anomalous dimensions - a1 : float - target coupling value - a0 : float - initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors + gamma_ns : numpy.ndarray + non-singlet anomalous dimensions + a1 : float + target coupling value + a0 : float + initial coupling value + aem : float + electromagnetic coupling value + nf : int + number of active flavors Returns ------- - e_ns^2 : complex - O(as3aem2) non-singlet exact EKO + e_ns^2 : complex + O(as3aem2) non-singlet exact EKO """ return np.exp( (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei_qed.j02_exact_qed(a1, a0, aem, nf) @@ -193,36 +189,35 @@ def as3aem2_exact(gamma_ns, a1, a0, aem, nf): def dispatcher( order, method, gamma_ns, a1, a0, aem_list, alphaem_running, nf, ev_op_iterations ): - """ - Determine used kernel and call it. + r"""Determine used kernel and call it. In LO we always use the exact solution. Parameters ---------- - order : tuple(int,int) - perturbation order - method : str - method - gamma_ns : numpy.ndarray - non-singlet anomalous dimensions - a1 : float - target coupling value - a0 : float - initial coupling value - aem_list : numpy.ndarray - electromagnetic coupling values - alphaem_running : Bool - running of alphaem - nf : int - number of active flavors - ev_op_iterations : int - number of evolution steps + order : tuple(int,int) + perturbation order + method : str + method + gamma_ns : numpy.ndarray + non-singlet anomalous dimensions + a1 : float + target coupling value + a0 : float + initial coupling value + aem_list : numpy.ndarray + electromagnetic coupling values + alphaem_running : Bool + running of alphaem + nf : int + number of active flavors + ev_op_iterations : int + number of evolution steps Returns ------- - e_ns : complex - non-singlet EKO + e_ns : complex + non-singlet EKO """ if not alphaem_running: aem = aem_list[0] @@ -235,28 +230,27 @@ def dispatcher( @nb.njit(cache=True) def fixed_alphaem_exact(order, gamma_ns, a1, a0, aem, nf): - """ - Compute exact solution for fixed alphaem. + """Compute exact solution for fixed alphaem. Parameters ---------- - order : tuple(int,int) - perturbation order - gamma_ns : numpy.ndarray - non-singlet anomalous dimensions - a1 : float - target coupling value - a0 : float - initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors + order : tuple(int,int) + perturbation order + gamma_ns : numpy.ndarray + non-singlet anomalous dimensions + a1 : float + target coupling value + a0 : float + initial coupling value + aem : float + electromagnetic coupling value + nf : int + number of active flavors Returns ------- - e_ns : complex - non-singlet EKO + e_ns : complex + non-singlet EKO """ if order[1] == 1: if order[0] == 1: @@ -277,30 +271,29 @@ def fixed_alphaem_exact(order, gamma_ns, a1, a0, aem, nf): @nb.njit(cache=True) def running_alphaem_exact(order, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations): - """ - Compute exact solution for running alphaem. + """Compute exact solution for running alphaem. Parameters ---------- - order : tuple(int,int) - perturbation order - gamma_ns : numpy.ndarray - non-singlet anomalous dimensions - a1 : float - target coupling value - a0 : float - initial coupling value - aem_list : numpy.ndarray - electromagnetic coupling values - nf : int - number of active flavors - ev_op_iterations : int - number of evolution steps + order : tuple(int,int) + perturbation order + gamma_ns : numpy.ndarray + non-singlet anomalous dimensions + a1 : float + target coupling value + a0 : float + initial coupling value + aem_list : numpy.ndarray + electromagnetic coupling values + nf : int + number of active flavors + ev_op_iterations : int + number of evolution steps Returns ------- - e_ns : complex - non-singlet EKO + e_ns : complex + non-singlet EKO """ a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) res = 1.0 diff --git a/src/eko/kernels/singlet_qed.py b/src/eko/kernels/singlet_qed.py index ed50dd1d7..dde7f8f81 100644 --- a/src/eko/kernels/singlet_qed.py +++ b/src/eko/kernels/singlet_qed.py @@ -13,25 +13,25 @@ def eko_iterate(gamma_singlet, a1, a0, aem_list, nf, order, ev_op_iterations): Parameters ---------- - gamma_singlet : numpy.ndarray - singlet anomalous dimensions matrices - a1 : float - target strong coupling value - a0 : float - initial strong coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors - order : tuple(int,int) - QCDxQED perturbative orders - ev_op_iterations : int - number of evolution steps + gamma_singlet : numpy.ndarray + singlet anomalous dimensions matrices + a1 : float + target strong coupling value + a0 : float + initial strong coupling value + aem : float + electromagnetic coupling value + nf : int + number of active flavors + order : tuple(int,int) + QCDxQED perturbative orders + ev_op_iterations : int + number of evolution steps Returns ------- - e_s^{order} : numpy.ndarray - singlet QEDxQCD iterated (exact) EKO + e_s^{order} : numpy.ndarray + singlet QEDxQCD iterated (exact) EKO """ a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) e = np.identity(4, np.complex_) @@ -68,34 +68,33 @@ def dispatcher( ev_op_iterations, ev_op_max_order, ): - """ - Determine used kernel and call it. + """Determine used kernel and call it. Parameters ---------- - order : tuple(int,int) - perturbative order - method : str - method - gamma_singlet : numpy.ndarray - singlet anomalous dimensions matrices - a1 : float - target coupling value - a0 : float - initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors - ev_op_iterations : int - number of evolution steps - ev_op_max_order : tuple(int,int) - perturbative expansion order of U + order : tuple(int,int) + perturbative order + method : str + method + gamma_singlet : numpy.ndarray + singlet anomalous dimensions matrices + a1 : float + target coupling value + a0 : float + initial coupling value + aem : float + electromagnetic coupling value + nf : int + number of active flavors + ev_op_iterations : int + number of evolution steps + ev_op_max_order : tuple(int,int) + perturbative expansion order of U Returns ------- - e_s : numpy.ndarray - singlet EKO + e_s : numpy.ndarray + singlet EKO """ if method in ["iterate-exact", "iterate-expanded"]: return eko_iterate(gamma_singlet, a1, a0, aem_list, nf, order, ev_op_iterations) From dd597848988325c4da2293b6e57f45365044d615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 1 Dec 2022 12:44:51 +0100 Subject: [PATCH 254/312] Enforce numpy docstring stile in constants --- src/eko/constants.py | 24 +++++++++++------------ src/eko/scale_variations/exponentiated.py | 23 +++++++++++----------- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/eko/constants.py b/src/eko/constants.py index ab2bdc873..b8b157efe 100644 --- a/src/eko/constants.py +++ b/src/eko/constants.py @@ -55,12 +55,12 @@ def uplike_flavors(nf): Parameters ---------- - nf : int - Number of active flavors + nf : int + Number of active flavors Returns ------- - nu : int + nu : int """ if nf not in range(2, 6 + 1): @@ -75,12 +75,12 @@ def e2avg(nf): Parameters ---------- - nf : int - Number of active flavors + nf : int + Number of active flavors Returns ------- - e2avg : float + e2avg : float """ nu = uplike_flavors(nf) @@ -94,12 +94,12 @@ def vue2m(nf): Parameters ---------- - nf : int - Number of active flavors + nf : int + Number of active flavors Returns ------- - vu * e2m : float + vu * e2m : float """ nu = uplike_flavors(nf) @@ -112,12 +112,12 @@ def vde2m(nf): Parameters ---------- - nf : int - Number of active flavors + nf : int + Number of active flavors Returns ------- - vd * e2m : float + vd * e2m : float """ nd = nf - uplike_flavors(nf) diff --git a/src/eko/scale_variations/exponentiated.py b/src/eko/scale_variations/exponentiated.py index 6dc1f2935..c8b943e8b 100644 --- a/src/eko/scale_variations/exponentiated.py +++ b/src/eko/scale_variations/exponentiated.py @@ -47,24 +47,23 @@ def gamma_variation(gamma, order, nf, L): @nb.njit(cache=True) def gamma_variation_qed(gamma, order, nf, L, alphaem_running): - """ - Adjust the anomalous dimensions with the scale variations. + """Adjust the anomalous dimensions with the scale variations. Parameters ---------- - gamma : numpy.ndarray - anomalous dimensions - order : tuple(int,int) - perturbation order - nf : int - number of active flavors - L : float - logarithmic ratio of factorization and renormalization scale + gamma : numpy.ndarray + anomalous dimensions + order : tuple(int,int) + perturbation order + nf : int + number of active flavors + L : float + logarithmic ratio of factorization and renormalization scale Returns ------- - gamma : numpy.ndarray - adjusted anomalous dimensions + gamma : numpy.ndarray + adjusted anomalous dimensions """ # if alphaem is fixed then only alphas is varied so gamma[0,1] and gamma[0,2] # don't get a variation while gamma[1,1] gets a variation that is O(as2aem1) From 6b377af9257aa1da688f5a91eb8074b4c410046f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 1 Dec 2022 12:48:41 +0100 Subject: [PATCH 255/312] Change raise error in coupling --- src/eko/couplings.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/eko/couplings.py b/src/eko/couplings.py index 8e3d1d8fd..f787cefd5 100644 --- a/src/eko/couplings.py +++ b/src/eko/couplings.py @@ -433,12 +433,12 @@ def __init__( ) if scale_ref <= 0: raise ValueError(f"scale_ref has to be positive - got {scale_ref}") - if order[0] not in [0, 1, 2, 3, 4]: - raise NotImplementedError("a_s beyond N3LO is not implemented") + if order[0] not in [1, 2, 3, 4]: + raise NotImplementedError( + "QCD perturbative order must be at least 1 and at most 4" + ) if order[1] not in [0, 1, 2]: raise NotImplementedError("a_em beyond NLO is not implemented") - if order[0] == 0: - raise ValueError("QCD evolution order must be at least 1 (PTO>=0)") self.order = tuple(order) if method not in ["expanded", "exact"]: raise ValueError(f"Unknown method {method}") From 14477f9c8a7996530b45d6831e1a0a09e0c0220d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 1 Dec 2022 12:55:52 +0100 Subject: [PATCH 256/312] Fix test_couplings --- tests/eko/test_couplings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/eko/test_couplings.py b/tests/eko/test_couplings.py index 32f7a888c..152677433 100644 --- a/tests/eko/test_couplings.py +++ b/tests/eko/test_couplings.py @@ -93,7 +93,7 @@ def test_init(self): threshold_holder.area_walls[1:-1], (1.0, 1.0, 1.0), ) - with pytest.raises(ValueError): + with pytest.raises(NotImplementedError): Couplings( [couplings_ref[0], couplings_ref[1]], scale_ref, From 88e7284b74e3899048b5eb6dfb50f16b583ed512 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 6 Dec 2022 09:54:01 +0100 Subject: [PATCH 257/312] Remove TODO --- src/eko/anomalous_dimensions/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index cd39b6652..3ca51f210 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -94,7 +94,6 @@ def exp_matrix(gamma): dim = gamma.shape[0] e = np.zeros((dim, dim, dim), np.complex_) exp = np.zeros((dim, dim), np.complex_) - # TODO check if this loop can be entirely cast to numpy for i in range(dim): e[i] = np.outer(v[:, i], v_inv[i]) exp += e[i] * np.exp(w[i]) From 197f323c0979bf22e35af545ed5bdd947ad93818 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 6 Dec 2022 14:54:54 +0100 Subject: [PATCH 258/312] Add TODO in the as2 anomalous dimensions --- src/eko/anomalous_dimensions/as2.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/eko/anomalous_dimensions/as2.py b/src/eko/anomalous_dimensions/as2.py index d8fd2271b..9d263eefa 100644 --- a/src/eko/anomalous_dimensions/as2.py +++ b/src/eko/anomalous_dimensions/as2.py @@ -37,6 +37,8 @@ def gamma_nsm(n, nf, sx): S2 = sx[1] # Here, Sp refers to S' ("s-prime") (german: "s-strich" or in Pegasus language: SSTR) # of :cite:`Gluck:1989ze` and NOT to the Spence function a.k.a. dilogarithm + # TODO : these harmonic sums are computed also for the QED sector then we can use + # the ones that are passed to the O(as1aem1) anomalous dimensions Sp1m = harmonics.S1((n - 1) / 2) Sp2m = harmonics.S2((n - 1) / 2) Sp3m = harmonics.S3((n - 1) / 2) From 378d5ff4499f7244dfa1627c95d9d5f9e84f7bb1 Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Tue, 6 Dec 2022 16:31:03 +0100 Subject: [PATCH 259/312] Improve flavors doc --- extras/.gitignore | 2 ++ src/eko/evolution_operator/flavors.py | 43 ++++++++++++++------------- 2 files changed, 25 insertions(+), 20 deletions(-) create mode 100644 extras/.gitignore diff --git a/extras/.gitignore b/extras/.gitignore new file mode 100644 index 000000000..877741c4e --- /dev/null +++ b/extras/.gitignore @@ -0,0 +1,2 @@ +*.pdf +*.aux diff --git a/src/eko/evolution_operator/flavors.py b/src/eko/evolution_operator/flavors.py index 29fddc422..016292160 100644 --- a/src/eko/evolution_operator/flavors.py +++ b/src/eko/evolution_operator/flavors.py @@ -12,16 +12,16 @@ def pids_from_intrinsic_evol(label, nf, normalize): The normalization of the weights is only needed for the output rotation: - if we want to build e.g. the singlet in the initial state we simply have to sum - to obtain :math:`S = u + \bar u + d + \bar d + \ldots` + to obtain :math:`\Sigma = u + \bar u + d + \bar d + \ldots` - if we want to rotate back in the output we have to *normalize* the weights: - e.g. in nf=3 :math:`u = \frac 1 6 S + \frac 1 6 V + \ldots` + e.g. in nf=3 :math:`u = \frac 1 6 \Sigma + \frac 1 6 V + \ldots` The normalization can only happen here since we're actively cutting out some flavor (according to ``nf``). Parameters ---------- - evol : str + label : str evolution label nf : int maximum number of light flavors @@ -30,7 +30,8 @@ def pids_from_intrinsic_evol(label, nf, normalize): Returns ------- - m : list + list(float) + list of weights """ try: evol_idx = br.evol_basis.index(label) @@ -107,7 +108,7 @@ def rotate_pm_to_flavor(label): Returns ------- - l : list(float) + list(float) list of weights """ # g and ph are unaltered @@ -136,13 +137,13 @@ def rotate_matching(nf, qed=False, inverse=False): nf : int number of active flavors in the higher patch: to activate T15, nf=4 qed : bool - use qed? + use QED? inverse : bool use inverse conditions? Returns ------- - l : dict + dict mapping in dot notation between the bases """ # the gluon and the photon do not care about new quarks @@ -176,7 +177,7 @@ def rotate_matching(nf, qed=False, inverse=False): ("S", "Sdelta", f"T{names[nf]}", f"{q}+"), ("V", "Vdelta", f"V{names[nf]}", f"{q}-"), ): - a, b, c, d, e, f = rotation_parameters(nf) + a, b, c, d, e, f = qed_rotation_parameters(nf) if inverse: den = -b * d + a * e - c * e + b * f l[f"{tot}.{tot}"] = -(c * e - b * f) / den @@ -213,28 +214,31 @@ def rotate_matching_inverse(nf, qed=False): nf : int number of active flavors in the higher patch: to activate T15, nf=4 qed : bool - use qed? + use QED? Returns ------- - l : dict + dict mapping in dot notation between the bases """ return rotate_matching(nf, qed, True) -def rotation_parameters(nf): - """Parameters of the basis rotation from (S, Sdelta, h+) into (S, Sdelta, T_i), or equivalentely for V, Vdelta, V_i, h-. +def qed_rotation_parameters(nf): + r"""Parameters of the QED basis rotation. + + From :math:`(\Sigma, \Sigma_{\Delta}, h_+)` into :math:`(\Sigma, \Sigma_{\Delta}, T_i^j)`, + or equivalentely for :math:`V, V_{\Delta}, V_i^j, h_-`. Parameters ---------- nf : int - number of active flavors in the higher patch: to activate T3u V3u nf=4 + number of active flavors in the higher patch: e.g. to activate :math:`T_3^u` or :math:`V_3^u` choose ``nf=4`` Returns ------- - a,b,c,d,e,f : float - Parameters of the rotation: Sdelta = a*S + b*Sdelta + c*h+, T_i = d*S + e*Sdelta + f*h+ + a,b,c,d,e,f : float + Parameters of the rotation: :math:`\Sigma_{\Delta} = a*\Sigma + b*\Sigma_{\Delta} + c*h_+, T_i = d*\Sigma + e*\Sigma_{\Delta} + f*h_+` """ nu_l = constants.uplike_flavors(nf - 1) nd_l = (nf - 1) - nu_l @@ -250,12 +254,10 @@ def rotation_parameters(nf): c = -1 d = nd_l / (nf - 1) e = -nu_l / (nf - 1) - if nf in [3, 4]: + if nf in [3, 4]: # s and c unlock T3d, T3u that have -h+ f = -1 - # s and c unlock T3d, T3u that have -h+ - elif nf in [5, 6]: + elif nf in [5, 6]: # b and t unlock T8d, T8u that have -2h+ f = -2 - # b and t unlock T8d, T8u that have -2h+ return a, b, c, d, e, f @@ -273,7 +275,8 @@ def pids_from_intrinsic_unified_evol(label, nf, normalize): Returns ------- - m : list + list(float) + list of weights """ if label in ["ph", "g", "S", "V"]: return pids_from_intrinsic_evol(label, nf, normalize) From 94048a48e29df69f2062a1bd29c0bdf27ef34c90 Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Tue, 6 Dec 2022 17:00:47 +0100 Subject: [PATCH 260/312] Add explicit flav tests --- tests/eko/test_ev_op_flavors.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/eko/test_ev_op_flavors.py b/tests/eko/test_ev_op_flavors.py index 75bc18bc5..81bb35fc4 100644 --- a/tests/eko/test_ev_op_flavors.py +++ b/tests/eko/test_ev_op_flavors.py @@ -122,6 +122,18 @@ def test_rotate_matching_qed(): m = flavors.rotate_matching(4, True) assert len(list(filter(lambda e: "c+" in e, m.keys()))) == 3 assert len(list(filter(lambda e: "b-" in e, m.keys()))) == 1 + # S' = S + c+ + np.testing.assert_allclose(m["S.S"], 1.0) + np.testing.assert_allclose(m["S.c+"], 1.0) + # T3u' = u+ - c+ = 1/3(S + Sdelta) - c+ + np.testing.assert_allclose(m["Tu3.S"], 1.0 / 3.0) + np.testing.assert_allclose(m["Tu3.Sdelta"], 1.0 / 3.0) + np.testing.assert_allclose(m["Tu3.c+"], -1.0) + # Sdelta' = u+ + c+ - d+ - s+ = 1/3(2 Sdelta - S) + c+ + np.testing.assert_allclose(m["Sdelta.Sdelta"], 2.0 / 3.0) + np.testing.assert_allclose(m["Sdelta.S"], -1.0 / 3.0) + np.testing.assert_allclose(m["Sdelta.c+"], +1.0) + m = flavors.rotate_matching(5, True) assert len(list(filter(lambda e: "b-" in e, m.keys()))) == 3 assert len(list(filter(lambda e: "t+" in e, m.keys()))) == 1 From b1a4dfbe43f68ec631c2c732594f85568ad81638 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 7 Dec 2022 15:58:20 +0100 Subject: [PATCH 261/312] Add documentation on qed matching and basis rotation --- doc/source/theory/Matching.rst | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/doc/source/theory/Matching.rst b/doc/source/theory/Matching.rst index 90a5449a2..3fe9806b0 100644 --- a/doc/source/theory/Matching.rst +++ b/doc/source/theory/Matching.rst @@ -135,3 +135,34 @@ EKO implements two different strategies to perform this operation, that can be s We emphasize that in the backward evolution, below the threshold, the remaining high quark PDFs are always intrinsic and do not evolve anymore. In fact, if the initial PDFs (above threshold) do contain an intrinsic contribution, this has to be evolved below the threshold otherwise momentum sum rules can be violated. + +QED Matching +------------ + +In the QED case the matching is changed only because of the change of the evolution basis, therefore the only different part will be the basis rotation. +In fact, the |OME| :math:`\mathbf{A}^{(n_f)}(\mu_{h}^2)` don't have |QED| corrections. The matching of the singlet sector is unchanged since it +remains the same with respect to the |QCD| case. The same happens for the matching of the valence component. All the elements :math:`V_i` and :math:`T_i` +are non-singlet components, therefore they are matched with :math:`A_{ns}`. In the end, the new components :math:`\Sigma_\Delta` and :math:`V_\Delta` are matched +with :math:`A_{ns}` since they are both non-singlets. + +QED basis rotation +------------------ + +For the basis rotation we have to consider that we are using the intrinsic unified evolution basis. Here it will be discussed only the rotation to be applied +to the sector :math:`(\Sigma, \Sigma_\Delta, T_i)`, being the rotation of the sector :math:`(V, V_\Delta, V_i)` completely equivalent. +The rotation matrix is given by: + +.. math :: + \begin{pmatrix} \Sigma_{(n_f)} \\ \Sigma_{\Delta,(n_f)} \\ T_{i,(nf)} \end{pmatrix}^{(n_f+1)} = + \begin{pmatrix} 1 & 0 & 1 \\ a(n_f) & b(n_f) & c(n_f) \\ d(n_f) & e(n_f) & f(n_f) \end{pmatrix} + \begin{pmatrix} \Sigma_{(n_f)} \\ \Sigma_{\Delta,(n_f)} \\ h^+ \end{pmatrix}^{(n_f)} + +where + +.. math :: + a(n_f) & = \frac{1}{n_f}\Bigl(\frac{n_d(n_f+1)}{n_u(n_f+1)}n_u(n_f)-n_d(n_f)\Bigr) \\ + b(n_f) & = \frac{n_f+1}{n_u(n_f+1)}\frac{n_u(n_f)}{n_f} \\ + c(n_f) & = \begin{cases} \frac{n_d(n_f+1)}{n_u(n_f+1)} \quad \text{if $h$ is up-like}\\-1 \quad \text{if $h$ is down-like}\end{cases} \\ + d(n_f) & = \begin{cases} &\frac{n_u(n_f)}{n_f} \quad \text{if $h$ is up-like ($n_f$=3,5)} \\ &\frac{n_d(n_f)}{n_f} \quad \text{if $h$ is down-like ($n_f$=2,4)} \end{cases} \\ + e(n_f) & = \begin{cases} &\frac{n_u(n_f)}{n_f} \quad \text{if $h$ is up-like} \\ &-\frac{n_u(n_f)}{n_f} \quad \text{if $h$ is down-like} \end{cases} \\ + f(n_f) & = \begin{cases} &-1\quad \text{if $h$ is $s$, $c$ ($n_f$=2,3)} \\ &-2 \quad \text{if $h$ is $b$, $t$ ($n_f$=4,5)} \end{cases} From 1e56cbfe9006538db107cd67cf186ec7b5d43fec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Sun, 11 Dec 2022 18:16:34 +0100 Subject: [PATCH 262/312] Remove unnecessary if statements --- src/eko/anomalous_dimensions/__init__.py | 35 +++++++------------ tests/eko/test_ad.py | 44 +++++++++++++----------- 2 files changed, 37 insertions(+), 42 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index 3ca51f210..bd5dae69c 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -282,21 +282,16 @@ def gamma_ns_qed(order, mode, n, nf): sx_ns_qed = harmonics.compute_qed_ns_cache(n, sx[0]) # now combine gamma_ns = np.zeros((order[0] + 1, order[1] + 1), np.complex_) - if order[0] >= 1: - gamma_ns[1, 0] = as1.gamma_ns(n, sx[0]) - if order[1] >= 1: - gamma_ns[0, 1] = choose_ns_ad_aem1(mode, n, sx) - if order[0] >= 1 and order[1] >= 1: - gamma_ns[1, 1] = choose_ns_ad_as1aem1(mode, n, sx, sx_ns_qed) + gamma_ns[1, 0] = as1.gamma_ns(n, sx[0]) + gamma_ns[0, 1] = choose_ns_ad_aem1(mode, n, sx) + gamma_ns[1, 1] = choose_ns_ad_as1aem1(mode, n, sx, sx_ns_qed) # NLO and beyond if order[0] >= 2: if mode in [10102, 10103]: gamma_ns[2, 0] = as2.gamma_nsp(n, nf, sx) # To fill the full valence vector in NNLO we need to add gamma_ns^1 explicitly here - elif mode in [10202, 10203]: - gamma_ns[2, 0] = as2.gamma_nsm(n, nf, sx) else: - raise NotImplementedError("Non-singlet sector is not implemented") + gamma_ns[2, 0] = as2.gamma_nsm(n, nf, sx) if order[1] >= 2: gamma_ns[0, 2] = choose_ns_ad_aem2(mode, n, nf, sx, sx_ns_qed) # NNLO and beyond @@ -329,8 +324,10 @@ def choose_ns_ad_aem1(mode, n, sx): """ if mode in [10102, 10202]: return constants.eu2 * aem1.gamma_ns(n, sx) - if mode in [10103, 10203]: + elif mode in [10103, 10203]: return constants.ed2 * aem1.gamma_ns(n, sx) + else: + raise NotImplementedError("Non-singlet sector is not implemented") @nb.njit(cache=True) @@ -428,12 +425,9 @@ def gamma_singlet_qed(order, n, nf): sx = harmonics.sx(n, max_weight=3) sx_ns_qed = harmonics.compute_qed_ns_cache(n, sx[0]) gamma_s = np.zeros((order[0] + 1, order[1] + 1, 4, 4), np.complex_) - if order[0] >= 1: - gamma_s[1, 0] = as1.gamma_singlet_qed(n, sx[0], nf) - if order[1] >= 1: - gamma_s[0, 1] = aem1.gamma_singlet(n, nf, sx) - if order[0] >= 1 and order[1] >= 1: - gamma_s[1, 1] = as1aem1.gamma_singlet(n, nf, sx, sx_ns_qed) + gamma_s[1, 0] = as1.gamma_singlet_qed(n, sx[0], nf) + gamma_s[0, 1] = aem1.gamma_singlet(n, nf, sx) + gamma_s[1, 1] = as1aem1.gamma_singlet(n, nf, sx, sx_ns_qed) if order[0] >= 2: gamma_s[2, 0] = as2.gamma_singlet_qed(n, nf, sx) if order[1] >= 2: @@ -480,12 +474,9 @@ def gamma_valence_qed(order, n, nf): sx = harmonics.sx(n, max_weight=3) sx_ns_qed = harmonics.compute_qed_ns_cache(n, sx[0]) gamma_v = np.zeros((order[0] + 1, order[1] + 1, 2, 2), np.complex_) - if order[0] >= 1: - gamma_v[1, 0] = as1.gamma_valence_qed(n, sx[0]) - if order[1] >= 1: - gamma_v[0, 1] = aem1.gamma_valence(n, nf, sx) - if order[0] >= 1 and order[1] >= 1: - gamma_v[1, 1] = as1aem1.gamma_valence(n, nf, sx, sx_ns_qed) + gamma_v[1, 0] = as1.gamma_valence_qed(n, sx[0]) + gamma_v[0, 1] = aem1.gamma_valence(n, nf, sx) + gamma_v[1, 1] = as1aem1.gamma_valence(n, nf, sx, sx_ns_qed) if order[0] >= 2: gamma_v[2, 0] = as2.gamma_valence_qed(n, nf, sx) if order[1] >= 2: diff --git a/tests/eko/test_ad.py b/tests/eko/test_ad.py index 4993bfc8f..dce1066ff 100644 --- a/tests/eko/test_ad.py +++ b/tests/eko/test_ad.py @@ -126,20 +126,24 @@ def test_gamma_ns_qed(): nf = 3 # aem1 assert_almost_equal( - ad.gamma_ns_qed((0, 1), br.non_singlet_pids_map["ns-u"], 1, nf), - np.zeros((1, 2)), + ad.gamma_ns_qed((1, 1), br.non_singlet_pids_map["ns-u"], 1, nf), + np.zeros((2, 2)), + decimal=5, ) assert_almost_equal( - ad.gamma_ns_qed((0, 1), br.non_singlet_pids_map["ns-d"], 1, nf), - np.zeros((1, 2)), + ad.gamma_ns_qed((1, 1), br.non_singlet_pids_map["ns-d"], 1, nf), + np.zeros((2, 2)), + decimal=5, ) assert_almost_equal( - ad.gamma_ns_qed((0, 1), br.non_singlet_pids_map["ns+u"], 1, nf), - np.zeros((1, 2)), + ad.gamma_ns_qed((1, 1), br.non_singlet_pids_map["ns+u"], 1, nf)[0, 1], + 0, + decimal=5, ) assert_almost_equal( - ad.gamma_ns_qed((0, 1), br.non_singlet_pids_map["ns+d"], 1, nf), - np.zeros((1, 2)), + ad.gamma_ns_qed((1, 1), br.non_singlet_pids_map["ns+d"], 1, nf)[0, 1], + 0, + decimal=5, ) # as1aem1 assert_almost_equal( @@ -154,35 +158,35 @@ def test_gamma_ns_qed(): ) # aem2 assert_almost_equal( - ad.gamma_ns_qed((0, 2), br.non_singlet_pids_map["ns-u"], 1, nf), - np.zeros((1, 3)), + ad.gamma_ns_qed((1, 2), br.non_singlet_pids_map["ns-u"], 1, nf), + np.zeros((2, 3)), decimal=5, ) assert_almost_equal( - ad.gamma_ns_qed((0, 2), br.non_singlet_pids_map["ns-d"], 1, nf), - np.zeros((1, 3)), + ad.gamma_ns_qed((1, 2), br.non_singlet_pids_map["ns-d"], 1, nf), + np.zeros((2, 3)), decimal=5, ) # as2 assert_almost_equal( - ad.gamma_ns_qed((2, 0), br.non_singlet_pids_map["ns-u"], 1, nf), - np.zeros((3, 1)), + ad.gamma_ns_qed((2, 1), br.non_singlet_pids_map["ns-u"], 1, nf), + np.zeros((3, 2)), decimal=5, ) assert_almost_equal( - ad.gamma_ns_qed((2, 0), br.non_singlet_pids_map["ns-d"], 1, nf), - np.zeros((3, 1)), + ad.gamma_ns_qed((2, 1), br.non_singlet_pids_map["ns-d"], 1, nf), + np.zeros((3, 2)), decimal=5, ) # as3 assert_almost_equal( - ad.gamma_ns_qed((3, 0), br.non_singlet_pids_map["ns-u"], 1, nf), - np.zeros((4, 1)), + ad.gamma_ns_qed((3, 1), br.non_singlet_pids_map["ns-u"], 1, nf), + np.zeros((4, 2)), decimal=3, ) assert_almost_equal( - ad.gamma_ns_qed((3, 0), br.non_singlet_pids_map["ns-d"], 1, nf), - np.zeros((4, 1)), + ad.gamma_ns_qed((3, 1), br.non_singlet_pids_map["ns-d"], 1, nf), + np.zeros((4, 2)), decimal=3, ) From 25970c7a5e2fe962e83c08630e07bd1249942c6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 9 Jan 2023 18:16:09 +0100 Subject: [PATCH 263/312] Rename Qedref QrefQED --- benchmarks/CT18_bench.py | 2 +- benchmarks/NNPDF_bench.py | 4 +- benchmarks/apfel_bench.py | 4 +- doc/source/code/IO.rst | 2 +- src/eko/couplings.py | 4 +- src/eko/io/runcards.py | 4 +- tests/eko/evolution_operator/test_init.py | 50 ----------------------- 7 files changed, 9 insertions(+), 61 deletions(-) diff --git a/benchmarks/CT18_bench.py b/benchmarks/CT18_bench.py index 28922d4ba..29f048337 100644 --- a/benchmarks/CT18_bench.py +++ b/benchmarks/CT18_bench.py @@ -12,6 +12,7 @@ base_theory = { "Qref": 91.1870, + "QrefQED": 91.1870, "mc": 1.3, "mb": 4.75, "mt": 172.0, @@ -64,7 +65,6 @@ def benchmark_nnlo_qed(self, Q0=1.295, Q2grid=(1e4,)): "Q0": Q0, "MaxNfPdf": 5, "MaxNfAs": 5, - "alphaem_running": True, } ) operator_card = {"Q2grid": list(Q2grid)} diff --git a/benchmarks/NNPDF_bench.py b/benchmarks/NNPDF_bench.py index b67fce412..e2b706f3e 100644 --- a/benchmarks/NNPDF_bench.py +++ b/benchmarks/NNPDF_bench.py @@ -72,9 +72,7 @@ def benchmark_nnlo(self, Q0=1.65, Q2grid=(100,)): "QED": 2, "Q0": Q0, } - theory_card.update( - {"ModEv": "iterate-exact", "FNS": "VFNS", "alphaem_running": True} - ) + theory_card.update({"ModEv": "iterate-exact", "FNS": "VFNS", "QrefQED": 91.2}) self.skip_pdfs = lambda _theory: [ -6, diff --git a/benchmarks/apfel_bench.py b/benchmarks/apfel_bench.py index 930438c17..a5e55f975 100644 --- a/benchmarks/apfel_bench.py +++ b/benchmarks/apfel_bench.py @@ -192,6 +192,7 @@ class BenchmarkFFNS_qed(ApfelBenchmark): ffns_theory = { "Qref": 91.1870, + "QrefQED": 91.1870, "mc": 1.3, "mb": 4.75, "mt": 172.0, @@ -208,7 +209,6 @@ class BenchmarkFFNS_qed(ApfelBenchmark): "Q0": 5.0, "alphas": 0.118000, "alphaqed": 0.007496, - "alphaem_running": True, } ffns_theory = tolist(ffns_theory) @@ -271,6 +271,7 @@ class BenchmarkVFNS_qed(ApfelBenchmark): vfns_theory = { "Qref": 91.1870, + "QrefQED": 91.1870, "mc": 1.3, "mb": 4.75, "mt": 172.0, @@ -286,7 +287,6 @@ class BenchmarkVFNS_qed(ApfelBenchmark): "Q0": 1.25, "alphas": 0.118000, "alphaqed": 0.007496, - "alphaem_running": True, } vfns_theory = tolist(vfns_theory) diff --git a/doc/source/code/IO.rst b/doc/source/code/IO.rst index 43fe6daa0..b4f1bbaf1 100644 --- a/doc/source/code/IO.rst +++ b/doc/source/code/IO.rst @@ -69,7 +69,7 @@ and environment. The benchmark settings are available at :mod:`banana.data.theor * - ``alphaqed`` - :py:obj:`float` - Reference value of the electromagnetic coupling :math:`\alpha_{em}`. - * - ``Qedref`` + * - ``QrefQED`` - :py:obj:`float` - Reference scale at which the ``alphaqed`` value is given (in GeV). * - ``HQ`` diff --git a/src/eko/couplings.py b/src/eko/couplings.py index 15a59138d..b00c51d72 100644 --- a/src/eko/couplings.py +++ b/src/eko/couplings.py @@ -8,7 +8,7 @@ """ import logging -from math import nan +from math import isnan from typing import List import numba as nb @@ -456,7 +456,7 @@ def assert_positive(name, var): max_nf = couplings.max_num_flavs matchings = list(thresholds_ratios) scheme_name = hqm_scheme.name - self.alphaem_running = False if couplings.alphaem.scale is nan else True + self.alphaem_running = False if isnan(couplings.alphaem.scale) else True # create new threshold object self.a_ref = np.array(couplings.values) / 4.0 / np.pi # convert to a_s and a_em diff --git a/src/eko/io/runcards.py b/src/eko/io/runcards.py index fb91443f6..aea82df0b 100644 --- a/src/eko/io/runcards.py +++ b/src/eko/io/runcards.py @@ -276,10 +276,10 @@ def heavies(pattern: str): new["order"] = [old["PTO"] + 1, old["QED"]] alphaem = self.fallback(old.get("alphaqed"), old.get("alphaem"), default=0.0) - if "Qedref" not in old: + if "QrefQED" not in old: qedref = nan else: - qedref = old["Qedref"] + qedref = old["QrefQED"] new["couplings"] = dict( alphas=(old["alphas"], old["Qref"]), alphaem=(alphaem, qedref), diff --git a/tests/eko/evolution_operator/test_init.py b/tests/eko/evolution_operator/test_init.py index ec8f5281e..db9174c91 100644 --- a/tests/eko/evolution_operator/test_init.py +++ b/tests/eko/evolution_operator/test_init.py @@ -417,56 +417,6 @@ def test_compute_parallel(self, monkeypatch, theory_ffns, operator_card, tmp_pat o.compute() self.check_lo(o) - # def test_aem_list(self): - # tcard = deepcopy(theory_card) - # ocard = deepcopy(operators_card) - # ocard["configs"]["n_integration_cores"] = 2 - # ocard["configs"]["ev_op_iterations"] = 10 - # for qcd in range(1, 3 + 1): - # for qed in range(1, 2 + 1): - # for q0 in [np.sqrt(2.0), 2.0, 4.5]: - # for q2to in ocard["Q2grid"]: - # for aem_running in [True, False]: - # tcard["order"] = (qcd, qed) - # tcard["alphaem_running"] = aem_running - # tcard["Q0"] = q0 - # g = OperatorGrid.from_dict( - # tcard, - # ocard, - # ThresholdsAtlas.from_dict(tcard), - # Couplings.from_dict(tcard), - # InterpolatorDispatcher( - # XGrid( - # operators_card["xgrid"], - # log=operators_card["configs"][ - # "interpolation_is_log" - # ], - # ), - # operators_card["configs"][ - # "interpolation_polynomial_degree" - # ], - # ), - # ) - # o = Operator(g.config, g.managers, 3, q0**2, q2to) - # couplings = Couplings.from_dict(tcard) - # aem_list = o.aem_list_as - # (a0, a1, _) = o.a_s - # ev_op_iterations = ocard["configs"]["ev_op_iterations"] - # as_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) - # as_l = as_steps[0] - # for step, as_h in enumerate(as_steps[1:]): - # as_half = (as_h + as_l) / 2.0 - # aem = couplings.compute_aem_as( - # tcard["alphaem"] / 4 / np.pi, - # tcard["alphas"] / 4 / np.pi, - # as_half, - # 3, - # ) - # np.testing.assert_allclose( - # aem, aem_list[step], rtol=1e-4 - # ) - # as_l = as_h - def check_lo(self, o): assert (br.non_singlet_pids_map["ns-"], 0) in o.op_members np.testing.assert_allclose( From 6a125d702424a1b2049fa8199849a752f5b4b2e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 10 Jan 2023 19:42:58 +0100 Subject: [PATCH 264/312] Fix typo in betaQCD in kernels --- src/eko/kernels/singlet_qed.py | 2 +- src/eko/kernels/valence_qed.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/eko/kernels/singlet_qed.py b/src/eko/kernels/singlet_qed.py index dde7f8f81..1cfac6b60 100644 --- a/src/eko/kernels/singlet_qed.py +++ b/src/eko/kernels/singlet_qed.py @@ -35,7 +35,7 @@ def eko_iterate(gamma_singlet, a1, a0, aem_list, nf, order, ev_op_iterations): """ a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) e = np.identity(4, np.complex_) - betaQCD = np.zeros((4, 3)) + betaQCD = np.zeros((4, 2)) for i in range(1, 3 + 1): betaQCD[i, 0] = beta.beta_qcd((i + 1, 0), nf) betaQCD[1, 1] = beta.beta_qcd((2, 1), nf) diff --git a/src/eko/kernels/valence_qed.py b/src/eko/kernels/valence_qed.py index f34918fef..1cf719ea4 100644 --- a/src/eko/kernels/valence_qed.py +++ b/src/eko/kernels/valence_qed.py @@ -36,7 +36,7 @@ def eko_iterate(gamma_valence, a1, a0, aem_list, nf, order, ev_op_iterations): """ a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) e = np.identity(2, np.complex_) - betaQCD = np.zeros((4, 3)) + betaQCD = np.zeros((4, 2)) for i in range(1, 3 + 1): betaQCD[i, 0] = beta.beta_qcd((i + 1, 0), nf) betaQCD[1, 1] = beta.beta_qcd((2, 1), nf) From 996595572922056500204fe8699d69a8599a5e46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 10 Jan 2023 19:45:37 +0100 Subject: [PATCH 265/312] Make beta dimension depend on order --- src/eko/kernels/singlet_qed.py | 2 +- src/eko/kernels/valence_qed.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/eko/kernels/singlet_qed.py b/src/eko/kernels/singlet_qed.py index 1cfac6b60..41fcb33fa 100644 --- a/src/eko/kernels/singlet_qed.py +++ b/src/eko/kernels/singlet_qed.py @@ -35,7 +35,7 @@ def eko_iterate(gamma_singlet, a1, a0, aem_list, nf, order, ev_op_iterations): """ a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) e = np.identity(4, np.complex_) - betaQCD = np.zeros((4, 2)) + betaQCD = np.zeros((order[0] + 1, 2)) for i in range(1, 3 + 1): betaQCD[i, 0] = beta.beta_qcd((i + 1, 0), nf) betaQCD[1, 1] = beta.beta_qcd((2, 1), nf) diff --git a/src/eko/kernels/valence_qed.py b/src/eko/kernels/valence_qed.py index 1cf719ea4..bb7e965ca 100644 --- a/src/eko/kernels/valence_qed.py +++ b/src/eko/kernels/valence_qed.py @@ -36,7 +36,7 @@ def eko_iterate(gamma_valence, a1, a0, aem_list, nf, order, ev_op_iterations): """ a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) e = np.identity(2, np.complex_) - betaQCD = np.zeros((4, 2)) + betaQCD = np.zeros((order[0] + 1, 2)) for i in range(1, 3 + 1): betaQCD[i, 0] = beta.beta_qcd((i + 1, 0), nf) betaQCD[1, 1] = beta.beta_qcd((2, 1), nf) From 3ee4fee322e8b92652b8a71a3e8c2df31a8316cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 10 Jan 2023 19:54:57 +0100 Subject: [PATCH 266/312] Fix tests --- src/eko/kernels/singlet_qed.py | 2 +- src/eko/kernels/valence_qed.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/eko/kernels/singlet_qed.py b/src/eko/kernels/singlet_qed.py index 41fcb33fa..1f183eb05 100644 --- a/src/eko/kernels/singlet_qed.py +++ b/src/eko/kernels/singlet_qed.py @@ -36,7 +36,7 @@ def eko_iterate(gamma_singlet, a1, a0, aem_list, nf, order, ev_op_iterations): a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) e = np.identity(4, np.complex_) betaQCD = np.zeros((order[0] + 1, 2)) - for i in range(1, 3 + 1): + for i in range(1, order[0] + 1): betaQCD[i, 0] = beta.beta_qcd((i + 1, 0), nf) betaQCD[1, 1] = beta.beta_qcd((2, 1), nf) for step in range(1, ev_op_iterations + 1): diff --git a/src/eko/kernels/valence_qed.py b/src/eko/kernels/valence_qed.py index bb7e965ca..5b6d5ced2 100644 --- a/src/eko/kernels/valence_qed.py +++ b/src/eko/kernels/valence_qed.py @@ -37,7 +37,7 @@ def eko_iterate(gamma_valence, a1, a0, aem_list, nf, order, ev_op_iterations): a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) e = np.identity(2, np.complex_) betaQCD = np.zeros((order[0] + 1, 2)) - for i in range(1, 3 + 1): + for i in range(1, order[0] + 1): betaQCD[i, 0] = beta.beta_qcd((i + 1, 0), nf) betaQCD[1, 1] = beta.beta_qcd((2, 1), nf) for step in range(1, ev_op_iterations + 1): From 20fa8ed605a66cf5fcecf9609687535859b24383 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 11 Jan 2023 10:42:30 +0100 Subject: [PATCH 267/312] Collect charge factors in one function --- src/eko/anomalous_dimensions/aem1.py | 8 ++--- src/eko/anomalous_dimensions/aem2.py | 3 +- src/eko/anomalous_dimensions/as1.py | 4 +-- src/eko/anomalous_dimensions/as1aem1.py | 8 ++--- src/eko/anomalous_dimensions/as2.py | 4 +-- src/eko/anomalous_dimensions/as3.py | 4 +-- src/eko/constants.py | 48 +++++-------------------- src/eko/kernels/singlet_qed.py | 2 +- src/eko/kernels/valence_qed.py | 2 +- 9 files changed, 23 insertions(+), 60 deletions(-) diff --git a/src/eko/anomalous_dimensions/aem1.py b/src/eko/anomalous_dimensions/aem1.py index d3c8762e2..6d43d5fe3 100644 --- a/src/eko/anomalous_dimensions/aem1.py +++ b/src/eko/anomalous_dimensions/aem1.py @@ -125,9 +125,7 @@ def gamma_singlet(N, nf, sx): gamma_gq : :math:`\\gamma_{gq}^{(0)}` gamma_gg : :math:`\\gamma_{gg}^{(0)}` """ - e2avg = constants.e2avg(nf) - vue2m = constants.vue2m(nf) - vde2m = constants.vde2m(nf) + e2avg, vue2m, vde2m = constants.charge_combinations(nf) e2delta = vde2m - vue2m + e2avg gamma_ph_q = gamma_phq(N) gamma_q_ph = gamma_qph(N, nf) @@ -188,9 +186,7 @@ def gamma_valence(N, nf, sx): gamma_gq : :math:`\\gamma_{gq}^{(0)}` gamma_gg : :math:`\\gamma_{gg}^{(0)}` """ - e2avg = constants.e2avg(nf) - vue2m = constants.vue2m(nf) - vde2m = constants.vde2m(nf) + e2avg, vue2m, vde2m = constants.charge_combinations(nf) e2delta = vde2m - vue2m + e2avg gamma_V_01 = np.array( [ diff --git a/src/eko/anomalous_dimensions/aem2.py b/src/eko/anomalous_dimensions/aem2.py index 05bb033ad..1bdce3753 100644 --- a/src/eko/anomalous_dimensions/aem2.py +++ b/src/eko/anomalous_dimensions/aem2.py @@ -357,7 +357,8 @@ def gamma_singlet(N, nf, sx, sx_ns_qed): nd = nf - nu vu = nu / nf vd = nd / nf - e2avg = constants.e2avg(nf) + e2m = constants.eu2 - constants.ed2 + e2avg = (nu * constants.eu2 + nd * constants.ed2) / nf e2m = constants.eu2 - constants.ed2 gamma_ph_u = gamma_phu(N, nf, sx) gamma_ph_d = gamma_phd(N, nf, sx) diff --git a/src/eko/anomalous_dimensions/as1.py b/src/eko/anomalous_dimensions/as1.py index 714a42e0e..2f1c986e2 100644 --- a/src/eko/anomalous_dimensions/as1.py +++ b/src/eko/anomalous_dimensions/as1.py @@ -138,7 +138,7 @@ def gamma_singlet(N, s1, nf): @nb.njit(cache=True) def gamma_singlet_qed(N, s1, nf): - r"""Compute the leading-order singlet anomalous dimension matrix. + r"""Compute the leading-order singlet anomalous dimension matrix for the unified evolution basis. .. math:: \\gamma_S^{(1,0)} = \\left(\begin{array}{cccc} @@ -184,7 +184,7 @@ def gamma_singlet_qed(N, s1, nf): @nb.njit(cache=True) def gamma_valence_qed(N, s1): - r"""Compute the leading-order valence anomalous dimension matrix. + r"""Compute the leading-order valence anomalous dimension matrix for the unified evolution basis. .. math:: \\gamma_V^{(1,0)} = \\left(\begin{array}{cc} diff --git a/src/eko/anomalous_dimensions/as1aem1.py b/src/eko/anomalous_dimensions/as1aem1.py index f3f9b8ba6..6d6d2c0e8 100644 --- a/src/eko/anomalous_dimensions/as1aem1.py +++ b/src/eko/anomalous_dimensions/as1aem1.py @@ -388,9 +388,7 @@ def gamma_singlet(N, nf, sx, sx_ns_qed): gamma_singlet : numpy.ndarray :math:`O(a_s^1a_{em}^1)` singlet anomalous dimension :math:`\\gamma_{S}^{(1,1)}(N,nf,sx)` """ - e2avg = constants.e2avg(nf) - vue2m = constants.vue2m(nf) - vde2m = constants.vde2m(nf) + e2avg, vue2m, vde2m = constants.charge_combinations(nf) e2delta = vde2m - vue2m + e2avg e2_tot = nf * e2avg gamma_g_q = gamma_gq(N, sx) @@ -448,9 +446,7 @@ def gamma_valence(N, nf, sx, sx_ns_qed): gamma_singlet : numpy.ndarray :math:`O(a_s^1a_{em}^1)` valence anomalous dimension :math:`\\gamma_{V}^{(1,1)}(N,nf,sx)` """ - e2avg = constants.e2avg(nf) - vue2m = constants.vue2m(nf) - vde2m = constants.vde2m(nf) + e2avg, vue2m, vde2m = constants.charge_combinations(nf) e2delta = vde2m - vue2m + e2avg gamma_V_11 = np.array( [ diff --git a/src/eko/anomalous_dimensions/as2.py b/src/eko/anomalous_dimensions/as2.py index 9d263eefa..88921af68 100644 --- a/src/eko/anomalous_dimensions/as2.py +++ b/src/eko/anomalous_dimensions/as2.py @@ -272,7 +272,7 @@ def gamma_singlet(n, nf, sx): @nb.njit(cache=True) def gamma_singlet_qed(N, nf, sx): - r"""Compute the leading-order singlet anomalous dimension matrix. + r"""Compute the leading-order singlet anomalous dimension matrix for the unified evolution basis. .. math:: \\gamma_S^{(2,0)} = \\left(\begin{array}{cccc} @@ -319,7 +319,7 @@ def gamma_singlet_qed(N, nf, sx): @nb.njit(cache=True) def gamma_valence_qed(N, nf, sx): - r"""Compute the leading-order valence anomalous dimension matrix. + r"""Compute the leading-order valence anomalous dimension matrix for the unified evolution basis. .. math:: \\gamma_V^{(2,0)} = \\left(\begin{array}{cc} diff --git a/src/eko/anomalous_dimensions/as3.py b/src/eko/anomalous_dimensions/as3.py index ac1dda445..4241847a9 100644 --- a/src/eko/anomalous_dimensions/as3.py +++ b/src/eko/anomalous_dimensions/as3.py @@ -581,7 +581,7 @@ def gamma_singlet(N, nf, sx): @nb.njit(cache=True) def gamma_singlet_qed(N, nf, sx): - r"""Compute the leading-order singlet anomalous dimension matrix. + r"""Compute the leading-order singlet anomalous dimension matrix for the unified evolution basis. .. math:: \\gamma_S^{(3,0)} = \\left(\begin{array}{cccc} @@ -628,7 +628,7 @@ def gamma_singlet_qed(N, nf, sx): @nb.njit(cache=True) def gamma_valence_qed(N, nf, sx): - r"""Compute the leading-order valence anomalous dimension matrix. + r"""Compute the leading-order valence anomalous dimension matrix for the unified evolution basis. .. math:: \\gamma_V^{(3,0)} = \\left(\begin{array}{cc} diff --git a/src/eko/constants.py b/src/eko/constants.py index b8b157efe..e8c4603b2 100644 --- a/src/eko/constants.py +++ b/src/eko/constants.py @@ -70,8 +70,9 @@ def uplike_flavors(nf): @nb.njit(cache=True) -def e2avg(nf): - """Compute the charge squared average. +def charge_combinations(nf): + """ + Compute the combination of charges. Parameters ---------- @@ -81,44 +82,13 @@ def e2avg(nf): Returns ------- e2avg : float + vue2m : float + vde2m : float """ nu = uplike_flavors(nf) nd = nf - nu - return (nu * eu2 + nd * ed2) / nf - - -@nb.njit(cache=True) -def vue2m(nf): - """Compute the product nu / nf * (e2u - e2d). - - Parameters - ---------- - nf : int - Number of active flavors - - Returns - ------- - vu * e2m : float - - """ - nu = uplike_flavors(nf) - return nu / nf * (eu2 - ed2) - - -@nb.njit(cache=True) -def vde2m(nf): - """Compute the product nd / nf * (e2u - e2d). - - Parameters - ---------- - nf : int - Number of active flavors - - Returns - ------- - vd * e2m : float - - """ - nd = nf - uplike_flavors(nf) - return nd / nf * (eu2 - ed2) + e2avg = (nu * eu2 + nd * ed2) / nf + vue2m = nu / nf * (eu2 - ed2) + vde2m = nd / nf * (eu2 - ed2) + return e2avg, vue2m, vde2m diff --git a/src/eko/kernels/singlet_qed.py b/src/eko/kernels/singlet_qed.py index 1f183eb05..7f882a2a2 100644 --- a/src/eko/kernels/singlet_qed.py +++ b/src/eko/kernels/singlet_qed.py @@ -35,7 +35,7 @@ def eko_iterate(gamma_singlet, a1, a0, aem_list, nf, order, ev_op_iterations): """ a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) e = np.identity(4, np.complex_) - betaQCD = np.zeros((order[0] + 1, 2)) + betaQCD = np.zeros((order[0] + 1, order[1] + 1)) for i in range(1, order[0] + 1): betaQCD[i, 0] = beta.beta_qcd((i + 1, 0), nf) betaQCD[1, 1] = beta.beta_qcd((2, 1), nf) diff --git a/src/eko/kernels/valence_qed.py b/src/eko/kernels/valence_qed.py index 5b6d5ced2..6a9b37f9a 100644 --- a/src/eko/kernels/valence_qed.py +++ b/src/eko/kernels/valence_qed.py @@ -36,7 +36,7 @@ def eko_iterate(gamma_valence, a1, a0, aem_list, nf, order, ev_op_iterations): """ a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) e = np.identity(2, np.complex_) - betaQCD = np.zeros((order[0] + 1, 2)) + betaQCD = np.zeros((order[0] + 1, order[1] + 1)) for i in range(1, order[0] + 1): betaQCD[i, 0] = beta.beta_qcd((i + 1, 0), nf) betaQCD[1, 1] = beta.beta_qcd((2, 1), nf) From e04bce3e702b35dca561bcfe9f0fe7c54a162a5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 11 Jan 2023 14:42:45 +0100 Subject: [PATCH 268/312] Reorganize evolution integrals --- src/eko/anomalous_dimensions/aem1.py | 4 +- src/eko/anomalous_dimensions/as1.py | 8 +- src/eko/anomalous_dimensions/as2.py | 8 +- src/eko/anomalous_dimensions/as3.py | 8 +- src/eko/kernels/evolution_integrals.py | 160 +++++++--- src/eko/kernels/evolution_integrals_qcd.py | 324 +++++++++++++++++++++ src/eko/kernels/evolution_integrals_qed.py | 74 ++--- src/eko/kernels/non_singlet.py | 29 +- src/eko/kernels/non_singlet_qed.py | 39 ++- src/eko/kernels/singlet.py | 2 +- tests/eko/kernels/test_as4_ei.py | 2 +- tests/eko/kernels/test_ei.py | 14 +- 12 files changed, 525 insertions(+), 147 deletions(-) create mode 100644 src/eko/kernels/evolution_integrals_qcd.py diff --git a/src/eko/anomalous_dimensions/aem1.py b/src/eko/anomalous_dimensions/aem1.py index 6d43d5fe3..b9b350fe0 100644 --- a/src/eko/anomalous_dimensions/aem1.py +++ b/src/eko/anomalous_dimensions/aem1.py @@ -94,7 +94,7 @@ def gamma_ns(N, sx): @nb.njit(cache=True) def gamma_singlet(N, nf, sx): - r"""Compute the leading-order singlet anomalous dimension matrix. + r"""Compute the QED leading-order singlet anomalous dimension matrix. .. math:: \\gamma_S^{(0)} = \\left(\begin{array}{cc} @@ -159,7 +159,7 @@ def gamma_singlet(N, nf, sx): @nb.njit(cache=True) def gamma_valence(N, nf, sx): - r"""Compute the leading-order valence anomalous dimension matrix. + r"""Compute the QED leading-order valence anomalous dimension matrix. .. math:: \\gamma_V^{(0,1)} = \\left(\begin{array}{cc} diff --git a/src/eko/anomalous_dimensions/as1.py b/src/eko/anomalous_dimensions/as1.py index 2f1c986e2..e79570d01 100644 --- a/src/eko/anomalous_dimensions/as1.py +++ b/src/eko/anomalous_dimensions/as1.py @@ -159,8 +159,8 @@ def gamma_singlet_qed(N, s1, nf): Returns ------- - gamma_S_0 : numpy.ndarray - Leading-order singlet anomalous dimension matrix :math:`\\gamma_{S}^{(0)}(N)` + gamma_S : numpy.ndarray + Leading-order singlet anomalous dimension matrix :math:`\\gamma_{S}^{(1,0)}(N)` See Also -------- @@ -201,8 +201,8 @@ def gamma_valence_qed(N, s1): Returns ------- - gamma_S_0 : numpy.ndarray - Leading-order singlet anomalous dimension matrix :math:`\\gamma_{S}^{(0)}(N)` + gamma_V : numpy.ndarray + Leading-order singlet anomalous dimension matrix :math:`\\gamma_{V}^{(1,0)}(N)` See Also -------- diff --git a/src/eko/anomalous_dimensions/as2.py b/src/eko/anomalous_dimensions/as2.py index 88921af68..5620dec7d 100644 --- a/src/eko/anomalous_dimensions/as2.py +++ b/src/eko/anomalous_dimensions/as2.py @@ -293,8 +293,8 @@ def gamma_singlet_qed(N, nf, sx): Returns ------- - gamma_S_0 : numpy.ndarray - Leading-order singlet anomalous dimension matrix :math:`\\gamma_{S}^{(0)}(N)` + gamma_S : numpy.ndarray + Leading-order singlet anomalous dimension matrix :math:`\\gamma_{S}^{(2,0)}(N)` See Also -------- @@ -338,8 +338,8 @@ def gamma_valence_qed(N, nf, sx): Returns ------- - gamma_S_0 : numpy.ndarray - Leading-order singlet anomalous dimension matrix :math:`\\gamma_{S}^{(0)}(N)` + gamma_V : numpy.ndarray + Leading-order singlet anomalous dimension matrix :math:`\\gamma_{V}^{(2,0)}(N)` See Also -------- diff --git a/src/eko/anomalous_dimensions/as3.py b/src/eko/anomalous_dimensions/as3.py index 4241847a9..023cf4dd2 100644 --- a/src/eko/anomalous_dimensions/as3.py +++ b/src/eko/anomalous_dimensions/as3.py @@ -602,8 +602,8 @@ def gamma_singlet_qed(N, nf, sx): Returns ------- - gamma_S_0 : numpy.ndarray - Leading-order singlet anomalous dimension matrix :math:`\\gamma_{S}^{(0)}(N)` + gamma_S : numpy.ndarray + Leading-order singlet anomalous dimension matrix :math:`\\gamma_{S}^{(3,0)}(N)` See Also -------- @@ -647,8 +647,8 @@ def gamma_valence_qed(N, nf, sx): Returns ------- - gamma_S_0 : numpy.ndarray - Leading-order singlet anomalous dimension matrix :math:`\\gamma_{S}^{(0)}(N)` + gamma_V : numpy.ndarray + Leading-order singlet anomalous dimension matrix :math:`\\gamma_{V}^{(3,0)}(N)` See Also -------- diff --git a/src/eko/kernels/evolution_integrals.py b/src/eko/kernels/evolution_integrals.py index 667adeb14..85ea37ae1 100644 --- a/src/eko/kernels/evolution_integrals.py +++ b/src/eko/kernels/evolution_integrals.py @@ -12,11 +12,36 @@ import numba as nb import numpy as np -from .. import beta + +@nb.njit(cache=True) +def jm10(a1, a0, beta0): + r"""LO-LO QED exact evolution integral. + + .. math:: + j^{(-1,0)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{(\beta_0 + aem \beta_{0,1}) a_s'^2} + = \frac{1.0 / a0 - 1.0 / as}{\beta_0 + aem \beta_{0,1}} + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + aem : float + electromagnetic coupling value + nf : int + number of active flavors + + Returns + ------- + j00 : float + integral + """ + return (1.0 / a0 - 1.0 / a1) / beta0 @nb.njit(cache=True) -def j00(a1, a0, nf): +def j00(a1, a0, beta0): r""" LO-LO exact evolution integral. @@ -38,11 +63,11 @@ def j00(a1, a0, nf): j00 : float integral """ - return np.log(a1 / a0) / beta.beta_qcd((2, 0), nf) + return np.log(a1 / a0) / beta0 @nb.njit(cache=True) -def j11_exact(a1, a0, nf): +def j11_exact(a1, a0, beta0, beta1): r""" NLO-NLO exact evolution integral. @@ -65,13 +90,12 @@ def j11_exact(a1, a0, nf): j11 : float integral """ - beta_qcd_as3 = beta.beta_qcd((3, 0), nf) - b1 = beta.b_qcd((3, 0), nf) - return (1.0 / beta_qcd_as3) * np.log((1.0 + a1 * b1) / (1.0 + a0 * b1)) + b1 = beta1 / beta0 + return (1.0 / beta1) * np.log((1.0 + a1 * b1) / (1.0 + a0 * b1)) @nb.njit(cache=True) -def j11_expanded(a1, a0, nf): +def j11_expanded(a1, a0, beta0): r""" NLO-NLO expanded evolution integral. @@ -92,11 +116,11 @@ def j11_expanded(a1, a0, nf): j11_exp : float integral """ - return 1.0 / beta.beta_qcd((2, 0), nf) * (a1 - a0) + return 1.0 / beta0 * (a1 - a0) @nb.njit(cache=True) -def j01_exact(a1, a0, nf): +def j01_exact(a1, a0, beta0, beta1): r""" LO-NLO exact evolution integral. @@ -119,11 +143,12 @@ def j01_exact(a1, a0, nf): j11 : float integral """ - return j00(a1, a0, nf) - beta.b_qcd((3, 0), nf) * j11_exact(a1, a0, nf) + b1 = beta1 / beta0 + return j00(a1, a0, beta0) - b1 * j11_exact(a1, a0, beta0, beta1) @nb.njit(cache=True) -def j01_expanded(a1, a0, nf): +def j01_expanded(a1, a0, beta0, beta1): r""" LO-NLO expanded evolution integral. @@ -144,11 +169,40 @@ def j01_expanded(a1, a0, nf): j01_exp : float integral """ - return j00(a1, a0, nf) - beta.b_qcd((3, 0), nf) * j11_expanded(a1, a0, nf) + b1 = beta1 / beta0 + return j00(a1, a0, beta0) - b1 * j11_expanded(a1, a0, beta0) + + +@nb.njit(cache=True) +def jm11_exact(a1, a0, beta0, beta1): + r"""LO-NLO exact evolution integral. + + .. math:: + j^{(-1,1)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3} + = \frac{1.0 / a0 - 1.0 / as}{\beta_0 + aem \beta_{0,1}} + \frac{b_1}{(\beta_0 + aem \beta_{0,1}} \left(\log(1 + 1 / (as b_1)) - \log(1 + 1 / (a0 b_1)\right) + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + + Returns + ------- + j11 : float + integral + """ + b1 = beta1 / beta0 + return -(1.0 / a1 - 1.0 / a0) / beta0 + b1 / beta0 * ( + np.log(1.0 + 1.0 / (a1 * b1)) - np.log(1.0 + 1.0 / (a0 * b1)) + ) @nb.njit(cache=True) -def j22_exact(a1, a0, nf): +def j22_exact(a1, a0, beta0, beta1, beta2): r""" NNLO-NNLO exact evolution integral. @@ -177,21 +231,19 @@ def j22_exact(a1, a0, nf): j22 : complex integral """ - b1 = beta.b_qcd((3, 0), nf) - b2 = beta.b_qcd((4, 0), nf) + b1 = beta1 / beta0 + b2 = beta2 / beta0 # allow Delta to be complex for nf = 6, the final result will be real Delta = np.sqrt(complex(4 * b2 - b1**2)) delta = np.arctan((b1 + 2 * a1 * b2) / Delta) - np.arctan( (b1 + 2 * a0 * b2) / Delta ) log = np.log((1 + a1 * (b1 + b2 * a1)) / (1 + a0 * (b1 + b2 * a0))) - return 1 / (2 * beta.beta_qcd((4, 0), nf)) * log - b1 / ( - beta.beta_qcd((4, 0), nf) - ) * np.real(delta / Delta) + return 1 / (2 * beta2) * log - b1 / (beta2) * np.real(delta / Delta) @nb.njit(cache=True) -def j12_exact(a1, a0, nf): +def j12_exact(a1, a0, beta0, beta1, beta2): r""" NLO-NNLO exact evolution integral. @@ -215,18 +267,18 @@ def j12_exact(a1, a0, nf): j12 : complex integral """ # pylint: disable=line-too-long - b1 = beta.b_qcd((3, 0), nf) - b2 = beta.b_qcd((4, 0), nf) + b1 = beta1 / beta0 + b2 = beta2 / beta0 # allow Delta to be complex for nf = 6, the final result will be real Delta = np.sqrt(complex(4 * b2 - b1**2)) delta = np.arctan((b1 + 2 * a1 * b2) / Delta) - np.arctan( (b1 + 2 * a0 * b2) / Delta ) - return 2.0 / (beta.beta_qcd((2, 0), nf)) * np.real(delta / Delta) + return 2.0 / (beta0) * np.real(delta / Delta) @nb.njit(cache=True) -def j02_exact(a1, a0, nf): +def j02_exact(a1, a0, beta0, beta1, beta2): r""" LO-NNLO exact evolution integral. @@ -249,15 +301,17 @@ def j02_exact(a1, a0, nf): j02 : complex integral """ + b1 = beta1 / beta0 + b2 = beta2 / beta0 return ( - j00(a1, a0, nf) - - beta.b_qcd((3, 0), nf) * j12_exact(a1, a0, nf) - - beta.b_qcd((4, 0), nf) * j22_exact(a1, a0, nf) + j00(a1, a0, beta0) + - b1 * j12_exact(a1, a0, beta0, beta1, beta2) + - b2 * j22_exact(a1, a0, beta0, beta1, beta2) ) @nb.njit(cache=True) -def j22_expanded(a1, a0, nf): +def j22_expanded(a1, a0, beta0): r""" NNLO-NNLO expanded evolution integral. @@ -278,11 +332,11 @@ def j22_expanded(a1, a0, nf): j22_exp : float integral """ - return 1 / (2 * beta.beta_qcd((2, 0), nf)) * (a1**2 - a0**2) + return 1 / (2 * beta0) * (a1**2 - a0**2) @nb.njit(cache=True) -def j12_expanded(a1, a0, nf): +def j12_expanded(a1, a0, beta0, beta1): r""" NLO-NNLO expanded evolution integral. @@ -304,12 +358,12 @@ def j12_expanded(a1, a0, nf): j12_exp : float integral """ - b1 = beta.b_qcd((3, 0), nf) - return 1 / beta.beta_qcd((2, 0), nf) * (a1 - a0 - b1 / 2 * (a1**2 - a0**2)) + b1 = beta1 / beta0 + return 1 / beta0 * (a1 - a0 - b1 / 2 * (a1**2 - a0**2)) @nb.njit(cache=True) -def j02_expanded(a1, a0, nf): +def j02_expanded(a1, a0, beta0, beta1, beta2): r""" LO-NNLO expanded evolution integral. @@ -331,8 +385,42 @@ def j02_expanded(a1, a0, nf): j02_exp : float integral """ + b1 = beta1 / beta0 + b2 = beta2 / beta0 + return ( + j00(a1, a0, beta0) + - b1 * j12_expanded(a1, a0, beta0, beta1) + - b2 * j22_expanded(a1, a0, beta0) + ) + + +@nb.njit(cache=True) +def jm12_exact(a1, a0, beta0, beta1, beta2): + r"""LO-NNLO exact evolution integral. + + .. math:: + j^{(-1,2)}(a_s,a_s^0,aem) &= \int\limits_{a_s^0}^{a_s}\!da_s'\, + \frac{1}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ + &= j^{(-1,0)}(a_s,a_s^0,aem) - b_1 j^{(0,2)}(a_s,a_s^0) - b_2 j^{(1,2)}(a_s,a_s^0) + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + + Returns + ------- + j02 : complex + integral + """ + b1 = beta1 / beta0 + b2 = beta2 / beta0 return ( - j00(a1, a0, nf) - - beta.b_qcd((3, 0), nf) * j12_expanded(a1, a0, nf) - - beta.b_qcd((4, 0), nf) * j22_expanded(a1, a0, nf) + jm10(a1, a0, beta0) + - b1 * j02_exact(a1, a0, beta0, beta1, beta2) + - b2 * j12_exact(a1, a0, beta0, beta1, beta2) ) diff --git a/src/eko/kernels/evolution_integrals_qcd.py b/src/eko/kernels/evolution_integrals_qcd.py new file mode 100644 index 000000000..07ecfbae4 --- /dev/null +++ b/src/eko/kernels/evolution_integrals_qcd.py @@ -0,0 +1,324 @@ +r"""Compute evolution integrals needed for QED.""" +import numba as nb + +from .. import beta +from . import evolution_integrals as ei + + +@nb.njit(cache=True) +def j00(a1, a0, nf): + r""" + LO-LO exact evolution integral. + + .. math:: + j^{(0,0)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{\beta_0 a_s'} + = \frac{\ln(a_s/a_s^0)}{\beta_0} + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + + Returns + ------- + j00 : float + integral + """ + beta0 = beta.beta_qcd((2, 0), nf) + return ei.j00(a1, a0, beta0) + + +@nb.njit(cache=True) +def j11_exact(a1, a0, nf): + r""" + NLO-NLO exact evolution integral. + + .. math:: + j^{(1,1)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s}\!da_s'\, + \frac{a_s'^2}{\beta_0 a_s'^2 + \beta_1 a_s'^3} + = \frac{1}{\beta_1}\ln\left(\frac{1+b_1 a_s}{1+b_1 a_s^0}\right) + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + + Returns + ------- + j11 : float + integral + """ + beta0 = beta.beta_qcd((2, 0), nf) + beta1 = beta.beta_qcd((3, 0), nf) + return ei.j11_exact(a1, a0, beta0, beta1) + + +@nb.njit(cache=True) +def j11_expanded(a1, a0, nf): + r""" + NLO-NLO expanded evolution integral. + + .. math:: + j^{(1,1)}_{exp}(a_s,a_s^0) = \frac 1 {\beta_0}(a_s - a_s^0) + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + + Returns + ------- + j11_exp : float + integral + """ + beta0 = beta.beta_qcd((2, 0), nf) + return ei.j11_expanded(a1, a0, beta0) + + +@nb.njit(cache=True) +def j01_exact(a1, a0, nf): + r""" + LO-NLO exact evolution integral. + + .. math:: + j^{(0,1)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s}\!da_s'\, + \frac{a_s'}{\beta_0 a_s'^2 + \beta_1 a_s'^3} + = j^{(0,0)}(a_s,a_s^0) - b_1 j^{(1,1)}(a_s,a_s^0) + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + + Returns + ------- + j11 : float + integral + """ + beta0 = beta.beta_qcd((2, 0), nf) + beta1 = beta.beta_qcd((3, 0), nf) + return ei.j01_exact(a1, a0, beta0, beta1) + + +@nb.njit(cache=True) +def j01_expanded(a1, a0, nf): + r""" + LO-NLO expanded evolution integral. + + .. math:: + j^{(0,1)}_{exp}(a_s,a_s^0) = j^{(0,0)}(a_s,a_s^0) - b_1 j^{(1,1)}_{exp}(a_s,a_s^0) + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + + Returns + ------- + j01_exp : float + integral + """ + beta0 = beta.beta_qcd((2, 0), nf) + beta1 = beta.beta_qcd((3, 0), nf) + return ei.j01_expanded(a1, a0, beta0, beta1) + + +@nb.njit(cache=True) +def j22_exact(a1, a0, nf): + r""" + NNLO-NNLO exact evolution integral. + + .. math:: + j^{(2,2)}(a_s,a_s^0) &= + \int\limits_{a_s^0}^{a_s}\!da_s'\,\frac{a_s'^3} + {\beta_0 a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4} + = \frac{1}{\beta_2}\ln\left( + \frac{1 + a_s ( b_1 + b_2 a_s ) }{ 1 + a_s^0 ( b_1 + b_2 a_s^0 )}\right) + - \frac{b_1 \delta}{ \beta_2 \Delta} \\ + \delta &= \atan \left( \frac{b_1 + 2 a_s b_2 }{ \Delta} \right) + - \atan \left( \frac{b_1 + 2 a_s^0 b_2 }{ \Delta} \right) \\ + \Delta &= \sqrt{4 b_2 - b_1^2} + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + + Returns + ------- + j22 : complex + integral + """ + beta0 = beta.beta_qcd((2, 0), nf) + beta1 = beta.beta_qcd((3, 0), nf) + beta2 = beta.beta_qcd((4, 0), nf) + return ei.j22_exact(a1, a0, beta0, beta1, beta2) + + +@nb.njit(cache=True) +def j12_exact(a1, a0, nf): + r""" + NLO-NNLO exact evolution integral. + + .. math:: + j^{(1,2)}(a_s,a_s^0) &= \int\limits_{a_s^0}^{a_s}\!da_s'\,\frac{a_s'^2}{\beta_0 a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ + &= \frac{2 \delta}{\beta_0 \Delta} \\ + \delta &= \atan \left( \frac{b_1 + 2 a_s b_2 }{ \Delta} \right) - \atan \left( \frac{b_1 + 2 a_s^0 b_2 }{ \Delta} \right) \\ + \Delta &= \sqrt{4 b_2 - b_1^2} + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + + Returns + ------- + j12 : complex + integral + """ # pylint: disable=line-too-long + beta0 = beta.beta_qcd((2, 0), nf) + beta1 = beta.beta_qcd((3, 0), nf) + beta2 = beta.beta_qcd((4, 0), nf) + return ei.j12_exact(a1, a0, beta0, beta1, beta2) + + +@nb.njit(cache=True) +def j02_exact(a1, a0, nf): + r""" + LO-NNLO exact evolution integral. + + .. math:: + j^{(0,2)}(a_s,a_s^0) &= \int\limits_{a_s^0}^{a_s}\!da_s'\, + \frac{a_s'}{\beta_0 a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ + &= j^{(0,0)}(a_s,a_s^0) - b_1 j^{(1,2)}(a_s,a_s^0) - b_2 j^{(2,2)}(a_s,a_s^0) + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + + Returns + ------- + j02 : complex + integral + """ + beta0 = beta.beta_qcd((2, 0), nf) + beta1 = beta.beta_qcd((3, 0), nf) + beta2 = beta.beta_qcd((4, 0), nf) + return ei.j02_exact(a1, a0, beta0, beta1, beta2) + + +@nb.njit(cache=True) +def j22_expanded(a1, a0, nf): + r""" + NNLO-NNLO expanded evolution integral. + + .. math:: + j^{(2,2)}_{exp}(a_s,a_s^0) = \frac{1}{2 \beta_0} \left( a_s^2 - (a_s^0)^{2} \right) + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + + Returns + ------- + j22_exp : float + integral + """ + beta0 = beta.beta_qcd((2, 0), nf) + return ei.j22_expanded(a1, a0, beta0) + + +@nb.njit(cache=True) +def j12_expanded(a1, a0, nf): + r""" + NLO-NNLO expanded evolution integral. + + .. math:: + j^{(1,2)}_{exp}(a_s,a_s^0) = \frac{1}{\beta_0}\left[ a_s - a_s^0 - + \frac{b_1}{2} \left( a_s^2 - (a_s^0)^{2} \right)\right] + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + + Returns + ------- + j12_exp : float + integral + """ + beta0 = beta.beta_qcd((2, 0), nf) + beta1 = beta.beta_qcd((3, 0), nf) + beta2 = beta.beta_qcd((4, 0), nf) + return ei.j12_exact(a1, a0, beta0, beta1, beta2) + + +@nb.njit(cache=True) +def j02_expanded(a1, a0, nf): + r""" + LO-NNLO expanded evolution integral. + + .. math:: + j^{(0,2)}_{exp}(a_s,a_s^0) = j^{(0,0)}(a_s,a_s^0) - b_1 j^{(1,2)}_{exp}(a_s,a_s^0) + - b_2 j^{(2,2)}_{exp}(a_s,a_s^0) + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + nf : int + number of active flavors + + Returns + ------- + j02_exp : float + integral + """ + beta0 = beta.beta_qcd((2, 0), nf) + beta1 = beta.beta_qcd((3, 0), nf) + beta2 = beta.beta_qcd((4, 0), nf) + return ei.j02_expanded(a1, a0, beta0, beta1, beta2) diff --git a/src/eko/kernels/evolution_integrals_qed.py b/src/eko/kernels/evolution_integrals_qed.py index a70d2fc8b..3f22f7945 100644 --- a/src/eko/kernels/evolution_integrals_qed.py +++ b/src/eko/kernels/evolution_integrals_qed.py @@ -1,13 +1,12 @@ r"""Compute evolution integrals needed for QED.""" import numba as nb -import numpy as np from .. import beta from . import evolution_integrals as ei @nb.njit(cache=True) -def j00_qed(a1, a0, aem, nf): +def j00(a1, a0, aem, nf): r"""LO-LO QED exact evolution integral. .. math:: @@ -31,7 +30,7 @@ def j00_qed(a1, a0, aem, nf): integral """ beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) - return np.log(a1 / a0) / beta0 + return ei.j00(a1, a0, beta0) @nb.njit(cache=True) @@ -59,11 +58,11 @@ def jm10(a1, a0, aem, nf): integral """ beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) - return (1.0 / a0 - 1.0 / a1) / beta0 + return ei.jm10(a1, a0, beta0) @nb.njit(cache=True) -def j11_exact_qed(a1, a0, aem, nf): +def j11_exact(a1, a0, aem, nf): r"""NLO-NLO exact evolution integral. .. math:: @@ -89,12 +88,11 @@ def j11_exact_qed(a1, a0, aem, nf): """ beta1 = beta.beta_qcd((3, 0), nf) beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) - b1 = beta1 / beta0 - return (1.0 / beta1) * np.log((1.0 + a1 * b1) / (1.0 + a0 * b1)) + return ei.j11_exact(a1, a0, beta0, beta1) @nb.njit(cache=True) -def j01_exact_qed(a1, a0, aem, nf): +def j01_exact(a1, a0, aem, nf): r"""LO-NLO QED exact evolution integral. .. math:: @@ -120,8 +118,7 @@ def j01_exact_qed(a1, a0, aem, nf): """ beta1 = beta.beta_qcd((3, 0), nf) beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) - b1 = beta1 / beta0 - return j00_qed(a1, a0, aem, nf) - b1 * j11_exact_qed(a1, a0, aem, nf) + return ei.j01_exact(a1, a0, beta0, beta1) @nb.njit(cache=True) @@ -147,14 +144,12 @@ def jm11_exact(a1, a0, aem, nf): integral """ beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) - b1 = beta.beta_qcd((3, 0), nf) / beta0 - return -(1.0 / a1 - 1.0 / a0) / beta0 + b1 / beta0 * ( - np.log(1.0 + 1.0 / (a1 * b1)) - np.log(1.0 + 1.0 / (a0 * b1)) - ) + beta1 = beta.beta_qcd((3, 0), nf) + return ei.jm11_exact(a1, a0, beta0, beta1) @nb.njit(cache=True) -def j22_exact_qed(a1, a0, aem, nf): +def j22_exact(a1, a0, aem, nf): r"""NNLO-NNLO exact evolution integral. .. math:: @@ -185,21 +180,13 @@ def j22_exact_qed(a1, a0, aem, nf): integral """ beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) - b1 = beta.beta_qcd((3, 0), nf) / beta0 - b2 = beta.beta_qcd((4, 0), nf) / beta0 - # allow Delta to be complex for nf = 6, the final result will be real - Delta = np.sqrt(complex(4 * b2 - b1**2)) - delta = np.arctan((b1 + 2 * a1 * b2) / Delta) - np.arctan( - (b1 + 2 * a0 * b2) / Delta - ) - log = np.log((1 + a1 * (b1 + b2 * a1)) / (1 + a0 * (b1 + b2 * a0))) - return 1 / (2 * beta.beta_qcd((4, 0), nf)) * log - b1 / ( - beta.beta_qcd((4, 0), nf) - ) * np.real(delta / Delta) + beta1 = beta.beta_qcd((3, 0), nf) + beta2 = beta.beta_qcd((4, 0), nf) + return ei.j22_exact(a1, a0, beta0, beta1, beta2) @nb.njit(cache=True) -def j12_exact_qed(a1, a0, aem, nf): +def j12_exact(a1, a0, aem, nf): r"""NLO-NNLO exact evolution integral. .. math:: @@ -225,18 +212,13 @@ def j12_exact_qed(a1, a0, aem, nf): integral """ # pylint: disable=line-too-long beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) - b1 = beta.beta_qcd((3, 0), nf) / beta0 - b2 = beta.beta_qcd((4, 0), nf) / beta0 - # allow Delta to be complex for nf = 6, the final result will be real - Delta = np.sqrt(complex(4 * b2 - b1**2)) - delta = np.arctan((b1 + 2 * a1 * b2) / Delta) - np.arctan( - (b1 + 2 * a0 * b2) / Delta - ) - return 2.0 / (beta0) * np.real(delta / Delta) + beta1 = beta.beta_qcd((3, 0), nf) + beta2 = beta.beta_qcd((4, 0), nf) + return ei.j12_exact(a1, a0, beta0, beta1, beta2) @nb.njit(cache=True) -def j02_exact_qed(a1, a0, aem, nf): +def j02_exact(a1, a0, aem, nf): r"""LO-NNLO exact evolution integral. .. math:: @@ -261,13 +243,9 @@ def j02_exact_qed(a1, a0, aem, nf): integral """ beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) - b1 = beta.beta_qcd((3, 0), nf) / beta0 - b2 = beta.beta_qcd((4, 0), nf) / beta0 - return ( - j00_qed(a1, a0, aem, nf) - - b1 * j12_exact_qed(a1, a0, aem, nf) - - b2 * j22_exact_qed(a1, a0, aem, nf) - ) + beta1 = beta.beta_qcd((3, 0), nf) + beta2 = beta.beta_qcd((4, 0), nf) + return ei.j02_exact(a1, a0, beta0, beta1, beta2) @nb.njit(cache=True) @@ -294,10 +272,6 @@ def jm12_exact(a1, a0, aem, nf): integral """ beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) - b1 = beta.beta_qcd((3, 0), nf) / beta0 - b2 = beta.beta_qcd((4, 0), nf) / beta0 - return ( - jm10(a1, a0, aem, nf) - - b1 * j02_exact_qed(a1, a0, aem, nf) - - b2 * j12_exact_qed(a1, a0, aem, nf) - ) + beta1 = beta.beta_qcd((3, 0), nf) + beta2 = beta.beta_qcd((4, 0), nf) + return ei.jm12_exact(a1, a0, beta0, beta1, beta2) diff --git a/src/eko/kernels/non_singlet.py b/src/eko/kernels/non_singlet.py index ab9838572..a8bd3acdf 100644 --- a/src/eko/kernels/non_singlet.py +++ b/src/eko/kernels/non_singlet.py @@ -1,13 +1,11 @@ -""" -Collection of non-singlet EKOs. -""" +"""Collection of non-singlet EKOs.""" import numba as nb import numpy as np from .. import beta from . import as4_evolution_integrals as as4_ei -from . import evolution_integrals as ei +from . import evolution_integrals_qcd as ei from . import utils @@ -51,8 +49,7 @@ def U_vec(gamma_ns, nf, order): @nb.njit(cache=True) def lo_exact(gamma_ns, a1, a0, nf): - """ - |LO| non-singlet exact EKO + """|LO| non-singlet exact EKO. Parameters ---------- @@ -75,8 +72,7 @@ def lo_exact(gamma_ns, a1, a0, nf): @nb.njit(cache=True) def nlo_exact(gamma_ns, a1, a0, nf): - """ - |NLO| non-singlet exact EKO + """|NLO| non-singlet exact EKO. Parameters ---------- @@ -101,8 +97,7 @@ def nlo_exact(gamma_ns, a1, a0, nf): @nb.njit(cache=True) def nlo_expanded(gamma_ns, a1, a0, nf): - """ - |NLO| non-singlet expanded EKO + """|NLO| non-singlet expanded EKO. Parameters ---------- @@ -128,8 +123,7 @@ def nlo_expanded(gamma_ns, a1, a0, nf): @nb.njit(cache=True) def nnlo_exact(gamma_ns, a1, a0, nf): - """ - |NNLO| non-singlet exact EKO + """|NNLO| non-singlet exact EKO. Parameters ---------- @@ -156,8 +150,7 @@ def nnlo_exact(gamma_ns, a1, a0, nf): @nb.njit(cache=True) def nnlo_expanded(gamma_ns, a1, a0, nf): - """ - |NNLO| non-singlet expanded EKO + """|NNLO| non-singlet expanded EKO. Parameters ---------- @@ -184,7 +177,7 @@ def nnlo_expanded(gamma_ns, a1, a0, nf): @nb.njit(cache=True) def n3lo_expanded(gamma_ns, a1, a0, nf): - """|N3LO| non-singlet expanded EKO + """|N3LO| non-singlet expanded EKO. Parameters ---------- @@ -223,7 +216,7 @@ def n3lo_expanded(gamma_ns, a1, a0, nf): @nb.njit(cache=True) def n3lo_exact(gamma_ns, a1, a0, nf): - """|N3LO| non-singlet exact EKO + """|N3LO| non-singlet exact EKO. Parameters ---------- @@ -263,7 +256,7 @@ def n3lo_exact(gamma_ns, a1, a0, nf): @nb.njit(cache=True) def eko_ordered_truncated(gamma_ns, a1, a0, nf, order, ev_op_iterations): - """|NLO|, |NNLO| or |N3LO| non-singlet ordered truncated EKO + """|NLO|, |NNLO| or |N3LO| non-singlet ordered truncated EKO. Parameters ---------- @@ -303,7 +296,7 @@ def eko_ordered_truncated(gamma_ns, a1, a0, nf, order, ev_op_iterations): @nb.njit(cache=True) def eko_truncated(gamma_ns, a1, a0, nf, order, ev_op_iterations): - """|NLO|, |NNLO| or |N3LO| non-singlet truncated EKO + """|NLO|, |NNLO| or |N3LO| non-singlet truncated EKO. Parameters ---------- diff --git a/src/eko/kernels/non_singlet_qed.py b/src/eko/kernels/non_singlet_qed.py index d10ee6d56..8331aeba3 100644 --- a/src/eko/kernels/non_singlet_qed.py +++ b/src/eko/kernels/non_singlet_qed.py @@ -2,7 +2,7 @@ import numba as nb import numpy as np -from . import evolution_integrals_qed as ei_qed +from . import evolution_integrals_qed as ei from . import utils @@ -29,8 +29,8 @@ def as1aem1_exact(gamma_ns, a1, a0, aem, nf): O(as1aem1) non-singlet exact EKO """ return np.exp( - (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei_qed.j00_qed(a1, a0, aem, nf) - + aem * gamma_ns[0, 1] * ei_qed.jm10(a1, a0, aem, nf) + (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j00(a1, a0, aem, nf) + + aem * gamma_ns[0, 1] * ei.jm10(a1, a0, aem, nf) ) @@ -58,9 +58,8 @@ def as1aem2_exact(gamma_ns, a1, a0, aem, nf): O(as1aem2) non-singlet exact EKO """ return np.exp( - (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei_qed.j00_qed(a1, a0, aem, nf) - + (aem * gamma_ns[0, 1] + aem**2 * gamma_ns[0, 2]) - * ei_qed.jm10(a1, a0, aem, nf) + (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j00(a1, a0, aem, nf) + + (aem * gamma_ns[0, 1] + aem**2 * gamma_ns[0, 2]) * ei.jm10(a1, a0, aem, nf) ) @@ -87,9 +86,9 @@ def as2aem1_exact(gamma_ns, a1, a0, aem, nf): O(as2aem1) non-singlet exact EKO """ return np.exp( - (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei_qed.j01_exact_qed(a1, a0, aem, nf) - + gamma_ns[2, 0] * ei_qed.j11_exact_qed(a1, a0, aem, nf) - + aem * gamma_ns[0, 1] * ei_qed.jm11_exact(a1, a0, aem, nf) + (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j01_exact(a1, a0, aem, nf) + + gamma_ns[2, 0] * ei.j11_exact(a1, a0, aem, nf) + + aem * gamma_ns[0, 1] * ei.jm11_exact(a1, a0, aem, nf) ) @@ -116,10 +115,10 @@ def as2aem2_exact(gamma_ns, a1, a0, aem, nf): O(as2aem2) non-singlet exact EKO """ return np.exp( - (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei_qed.j01_exact_qed(a1, a0, aem, nf) - + gamma_ns[2, 0] * ei_qed.j11_exact_qed(a1, a0, aem, nf) + (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j01_exact(a1, a0, aem, nf) + + gamma_ns[2, 0] * ei.j11_exact(a1, a0, aem, nf) + (aem * gamma_ns[0, 1] + aem**2 * gamma_ns[0, 2]) - * ei_qed.jm11_exact(a1, a0, aem, nf) + * ei.jm11_exact(a1, a0, aem, nf) ) @@ -146,10 +145,10 @@ def as3aem1_exact(gamma_ns, a1, a0, aem, nf): O(as3aem1) non-singlet exact EKO """ return np.exp( - (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei_qed.j02_exact_qed(a1, a0, aem, nf) - + gamma_ns[2, 0] * ei_qed.j12_exact_qed(a1, a0, aem, nf) - + gamma_ns[3, 0] * ei_qed.j22_exact_qed(a1, a0, aem, nf) - + aem * gamma_ns[0, 1] * ei_qed.jm12_exact(a1, a0, aem, nf) + (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j02_exact(a1, a0, aem, nf) + + gamma_ns[2, 0] * ei.j12_exact(a1, a0, aem, nf) + + gamma_ns[3, 0] * ei.j22_exact(a1, a0, aem, nf) + + aem * gamma_ns[0, 1] * ei.jm12_exact(a1, a0, aem, nf) ) @@ -177,11 +176,11 @@ def as3aem2_exact(gamma_ns, a1, a0, aem, nf): O(as3aem2) non-singlet exact EKO """ return np.exp( - (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei_qed.j02_exact_qed(a1, a0, aem, nf) - + gamma_ns[2, 0] * ei_qed.j12_exact_qed(a1, a0, aem, nf) - + gamma_ns[3, 0] * ei_qed.j22_exact_qed(a1, a0, aem, nf) + (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j02_exact(a1, a0, aem, nf) + + gamma_ns[2, 0] * ei.j12_exact(a1, a0, aem, nf) + + gamma_ns[3, 0] * ei.j22_exact(a1, a0, aem, nf) + (aem * gamma_ns[0, 1] + aem**2 * gamma_ns[0, 2]) - * ei_qed.jm12_exact(a1, a0, aem, nf) + * ei.jm12_exact(a1, a0, aem, nf) ) diff --git a/src/eko/kernels/singlet.py b/src/eko/kernels/singlet.py index 8f120a9fa..03eda0398 100644 --- a/src/eko/kernels/singlet.py +++ b/src/eko/kernels/singlet.py @@ -6,7 +6,7 @@ from .. import anomalous_dimensions as ad from .. import beta from . import as4_evolution_integrals as as4_ei -from . import evolution_integrals as ei +from . import evolution_integrals_qcd as ei from . import utils diff --git a/tests/eko/kernels/test_as4_ei.py b/tests/eko/kernels/test_as4_ei.py index 95dc8a9ce..3b0f0c74a 100644 --- a/tests/eko/kernels/test_as4_ei.py +++ b/tests/eko/kernels/test_as4_ei.py @@ -2,7 +2,7 @@ from eko import beta from eko.kernels import as4_evolution_integrals as as4_ei -from eko.kernels.evolution_integrals import j00 +from eko.kernels.evolution_integrals_qcd import j00 def test_zero(): diff --git a/tests/eko/kernels/test_ei.py b/tests/eko/kernels/test_ei.py index 0058079a2..f64bd8dcb 100644 --- a/tests/eko/kernels/test_ei.py +++ b/tests/eko/kernels/test_ei.py @@ -1,7 +1,7 @@ import numpy as np from eko import beta -from eko.kernels import evolution_integrals as ei +from eko.kernels import evolution_integrals_qcd as ei from eko.kernels import evolution_integrals_qed as ei_qed @@ -28,14 +28,14 @@ def test_zero_qed(): """No evolution results in exp(0)""" nf = 3 for fnc in [ - ei_qed.j00_qed, + ei_qed.j00, ei_qed.jm10, - ei_qed.j11_exact_qed, - ei_qed.j01_exact_qed, + ei_qed.j11_exact, + ei_qed.j01_exact, ei_qed.jm11_exact, - ei_qed.j22_exact_qed, - ei_qed.j12_exact_qed, - ei_qed.j02_exact_qed, + ei_qed.j22_exact, + ei_qed.j12_exact, + ei_qed.j02_exact, ei_qed.jm12_exact, ]: np.testing.assert_allclose(fnc(1, 1, 0.00058, nf), 0) From f5d9d8e6a11d7c2de1ba05515d7c6f0f52d3f768 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 11 Jan 2023 14:45:45 +0100 Subject: [PATCH 269/312] Add e2delta to charge_combinations --- src/eko/anomalous_dimensions/aem1.py | 6 ++---- src/eko/anomalous_dimensions/as1aem1.py | 6 ++---- src/eko/constants.py | 3 ++- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/eko/anomalous_dimensions/aem1.py b/src/eko/anomalous_dimensions/aem1.py index b9b350fe0..5040169d1 100644 --- a/src/eko/anomalous_dimensions/aem1.py +++ b/src/eko/anomalous_dimensions/aem1.py @@ -125,8 +125,7 @@ def gamma_singlet(N, nf, sx): gamma_gq : :math:`\\gamma_{gq}^{(0)}` gamma_gg : :math:`\\gamma_{gg}^{(0)}` """ - e2avg, vue2m, vde2m = constants.charge_combinations(nf) - e2delta = vde2m - vue2m + e2avg + e2avg, vue2m, vde2m, e2delta = constants.charge_combinations(nf) gamma_ph_q = gamma_phq(N) gamma_q_ph = gamma_qph(N, nf) gamma_nonsinglet = gamma_ns(N, sx) @@ -186,8 +185,7 @@ def gamma_valence(N, nf, sx): gamma_gq : :math:`\\gamma_{gq}^{(0)}` gamma_gg : :math:`\\gamma_{gg}^{(0)}` """ - e2avg, vue2m, vde2m = constants.charge_combinations(nf) - e2delta = vde2m - vue2m + e2avg + e2avg, vue2m, vde2m, e2delta = constants.charge_combinations(nf) gamma_V_01 = np.array( [ [e2avg, vue2m], diff --git a/src/eko/anomalous_dimensions/as1aem1.py b/src/eko/anomalous_dimensions/as1aem1.py index 6d6d2c0e8..745f20449 100644 --- a/src/eko/anomalous_dimensions/as1aem1.py +++ b/src/eko/anomalous_dimensions/as1aem1.py @@ -388,8 +388,7 @@ def gamma_singlet(N, nf, sx, sx_ns_qed): gamma_singlet : numpy.ndarray :math:`O(a_s^1a_{em}^1)` singlet anomalous dimension :math:`\\gamma_{S}^{(1,1)}(N,nf,sx)` """ - e2avg, vue2m, vde2m = constants.charge_combinations(nf) - e2delta = vde2m - vue2m + e2avg + e2avg, vue2m, vde2m, e2delta = constants.charge_combinations(nf) e2_tot = nf * e2avg gamma_g_q = gamma_gq(N, sx) gamma_ph_q = gamma_phq(N, sx) @@ -446,8 +445,7 @@ def gamma_valence(N, nf, sx, sx_ns_qed): gamma_singlet : numpy.ndarray :math:`O(a_s^1a_{em}^1)` valence anomalous dimension :math:`\\gamma_{V}^{(1,1)}(N,nf,sx)` """ - e2avg, vue2m, vde2m = constants.charge_combinations(nf) - e2delta = vde2m - vue2m + e2avg + e2avg, vue2m, vde2m, e2delta = constants.charge_combinations(nf) gamma_V_11 = np.array( [ [e2avg, vue2m], diff --git a/src/eko/constants.py b/src/eko/constants.py index e8c4603b2..5c8f2b7ec 100644 --- a/src/eko/constants.py +++ b/src/eko/constants.py @@ -91,4 +91,5 @@ def charge_combinations(nf): e2avg = (nu * eu2 + nd * ed2) / nf vue2m = nu / nf * (eu2 - ed2) vde2m = nd / nf * (eu2 - ed2) - return e2avg, vue2m, vde2m + e2delta = vde2m - vue2m + e2avg + return e2avg, vue2m, vde2m, e2delta From 50f86987c5562d1e6b0bcb501bcea213ddc34210 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 11 Jan 2023 14:51:42 +0100 Subject: [PATCH 270/312] Add documentation for unified_evol_basis_pids --- src/eko/basis_rotation.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/eko/basis_rotation.py b/src/eko/basis_rotation.py index 52ebba561..4b972592c 100644 --- a/src/eko/basis_rotation.py +++ b/src/eko/basis_rotation.py @@ -96,7 +96,12 @@ + [108 + 2, 208 + 2] + [108 + 1, 208 + 1] ) -"""|pid| representation of :data:`unified_evol_basis`.""" +r""" +|pid| representation of :data:`unified_evol_basis`. + +The notation used for the non singlet compunents is the following: +pid_ns(u) = pid_ns + 1, pid_ns(d) = pid_ns + 2. +""" non_singlet_pids_map = { "ns-": 10201, From c043cca845610c2aa5c9d3010080a37fffabee26 Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Thu, 12 Jan 2023 17:50:21 +0100 Subject: [PATCH 271/312] Raise error symmetricaly --- src/eko/anomalous_dimensions/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/eko/anomalous_dimensions/__init__.py b/src/eko/anomalous_dimensions/__init__.py index bd5dae69c..f669865c6 100644 --- a/src/eko/anomalous_dimensions/__init__.py +++ b/src/eko/anomalous_dimensions/__init__.py @@ -326,8 +326,7 @@ def choose_ns_ad_aem1(mode, n, sx): return constants.eu2 * aem1.gamma_ns(n, sx) elif mode in [10103, 10203]: return constants.ed2 * aem1.gamma_ns(n, sx) - else: - raise NotImplementedError("Non-singlet sector is not implemented") + raise NotImplementedError("Non-singlet sector is not implemented") @nb.njit(cache=True) @@ -357,6 +356,7 @@ def choose_ns_ad_as1aem1(mode, n, sx, sx_ns_qed): return constants.eu2 * as1aem1.gamma_nsm(n, sx, sx_ns_qed) elif mode == 10203: return constants.ed2 * as1aem1.gamma_nsm(n, sx, sx_ns_qed) + raise NotImplementedError("Non-singlet sector is not implemented") @nb.njit(cache=True) @@ -386,6 +386,7 @@ def choose_ns_ad_aem2(mode, n, nf, sx, sx_ns_qed): return constants.eu2 * aem2.gamma_nsmu(n, nf, sx, sx_ns_qed) elif mode == 10203: return constants.ed2 * aem2.gamma_nsmd(n, nf, sx, sx_ns_qed) + raise NotImplementedError("Non-singlet sector is not implemented") @nb.njit(cache=True) From b98f932db6d6e19fef7606d2f71865ba611321f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 17 Jan 2023 12:13:12 +0100 Subject: [PATCH 272/312] Improve docstrings of evoluton_integrals --- src/eko/kernels/evolution_integrals.py | 96 +++++++++++++++++--------- 1 file changed, 62 insertions(+), 34 deletions(-) diff --git a/src/eko/kernels/evolution_integrals.py b/src/eko/kernels/evolution_integrals.py index 85ea37ae1..f1d22bc3e 100644 --- a/src/eko/kernels/evolution_integrals.py +++ b/src/eko/kernels/evolution_integrals.py @@ -27,14 +27,12 @@ def jm10(a1, a0, beta0): target coupling value a0 : float initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors + beta0 : float + LO beta function Returns ------- - j00 : float + jm10 : float integral """ return (1.0 / a0 - 1.0 / a1) / beta0 @@ -55,8 +53,8 @@ def j00(a1, a0, beta0): target coupling value a0 : float initial coupling value - nf : int - number of active flavors + beta0 : float + LO beta function Returns ------- @@ -82,8 +80,10 @@ def j11_exact(a1, a0, beta0, beta1): target coupling value a0 : float initial coupling value - nf : int - number of active flavors + beta0 : float + LO beta function + beta1 : float + NLO beta function Returns ------- @@ -108,8 +108,8 @@ def j11_expanded(a1, a0, beta0): target coupling value a0 : float initial coupling value - nf : int - number of active flavors + beta0 : float + LO beta function Returns ------- @@ -135,12 +135,14 @@ def j01_exact(a1, a0, beta0, beta1): target coupling value a0 : float initial coupling value - nf : int - number of active flavors + beta0 : float + LO beta function + beta1 : float + NLO beta function Returns ------- - j11 : float + j01 : float integral """ b1 = beta1 / beta0 @@ -161,8 +163,10 @@ def j01_expanded(a1, a0, beta0, beta1): target coupling value a0 : float initial coupling value - nf : int - number of active flavors + beta0 : float + LO beta function + beta1 : float + NLO beta function Returns ------- @@ -187,12 +191,14 @@ def jm11_exact(a1, a0, beta0, beta1): target coupling value a0 : float initial coupling value - nf : int - number of active flavors + beta0 : float + LO beta function + beta1 : float + NLO beta function Returns ------- - j11 : float + jm11 : float integral """ b1 = beta1 / beta0 @@ -223,8 +229,12 @@ def j22_exact(a1, a0, beta0, beta1, beta2): target coupling value a0 : float initial coupling value - nf : int - number of active flavors + beta0 : float + LO beta function + beta1 : float + NLO beta function + beta2 : float + NNLO beta function Returns ------- @@ -259,8 +269,12 @@ def j12_exact(a1, a0, beta0, beta1, beta2): target coupling value a0 : float initial coupling value - nf : int - number of active flavors + beta0 : float + LO beta function + beta1 : float + NLO beta function + beta2 : float + NNLO beta function Returns ------- @@ -293,8 +307,12 @@ def j02_exact(a1, a0, beta0, beta1, beta2): target coupling value a0 : float initial coupling value - nf : int - number of active flavors + beta0 : float + LO beta function + beta1 : float + NLO beta function + beta2 : float + NNLO beta function Returns ------- @@ -324,8 +342,8 @@ def j22_expanded(a1, a0, beta0): target coupling value a0 : float initial coupling value - nf : int - number of active flavors + beta0 : float + LO beta function Returns ------- @@ -350,8 +368,10 @@ def j12_expanded(a1, a0, beta0, beta1): target coupling value a0 : float initial coupling value - nf : int - number of active flavors + beta0 : float + LO beta function + beta1 : float + NLO beta function Returns ------- @@ -377,8 +397,12 @@ def j02_expanded(a1, a0, beta0, beta1, beta2): target coupling value a0 : float initial coupling value - nf : int - number of active flavors + beta0 : float + LO beta function + beta1 : float + NLO beta function + beta2 : float + NNLO beta function Returns ------- @@ -409,12 +433,16 @@ def jm12_exact(a1, a0, beta0, beta1, beta2): target coupling value a0 : float initial coupling value - nf : int - number of active flavors + beta0 : float + LO beta function + beta1 : float + NLO beta function + beta2 : float + NNLO beta function Returns ------- - j02 : complex + jm12 : complex integral """ b1 = beta1 / beta0 From d63e2e5e0fe32a48df8258e5355dcd29da45dd0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 17 Jan 2023 12:40:59 +0100 Subject: [PATCH 273/312] Fix docstrings --- src/eko/kernels/evolution_integrals.py | 58 +++++++++++++------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/src/eko/kernels/evolution_integrals.py b/src/eko/kernels/evolution_integrals.py index f1d22bc3e..a023904bc 100644 --- a/src/eko/kernels/evolution_integrals.py +++ b/src/eko/kernels/evolution_integrals.py @@ -28,7 +28,7 @@ def jm10(a1, a0, beta0): a0 : float initial coupling value beta0 : float - LO beta function + LO beta function coefficient Returns ------- @@ -54,7 +54,7 @@ def j00(a1, a0, beta0): a0 : float initial coupling value beta0 : float - LO beta function + LO beta function coefficient Returns ------- @@ -81,9 +81,9 @@ def j11_exact(a1, a0, beta0, beta1): a0 : float initial coupling value beta0 : float - LO beta function + LO beta function coefficient beta1 : float - NLO beta function + NLO beta function coefficient Returns ------- @@ -109,7 +109,7 @@ def j11_expanded(a1, a0, beta0): a0 : float initial coupling value beta0 : float - LO beta function + LO beta function coefficient Returns ------- @@ -136,9 +136,9 @@ def j01_exact(a1, a0, beta0, beta1): a0 : float initial coupling value beta0 : float - LO beta function + LO beta function coefficient beta1 : float - NLO beta function + NLO beta function coefficient Returns ------- @@ -164,9 +164,9 @@ def j01_expanded(a1, a0, beta0, beta1): a0 : float initial coupling value beta0 : float - LO beta function + LO beta function coefficient beta1 : float - NLO beta function + NLO beta function coefficient Returns ------- @@ -192,9 +192,9 @@ def jm11_exact(a1, a0, beta0, beta1): a0 : float initial coupling value beta0 : float - LO beta function + LO beta function coefficient beta1 : float - NLO beta function + NLO beta function coefficient Returns ------- @@ -230,11 +230,11 @@ def j22_exact(a1, a0, beta0, beta1, beta2): a0 : float initial coupling value beta0 : float - LO beta function + LO beta function coefficient beta1 : float - NLO beta function + NLO beta function coefficient beta2 : float - NNLO beta function + NNLO beta function coefficient Returns ------- @@ -270,11 +270,11 @@ def j12_exact(a1, a0, beta0, beta1, beta2): a0 : float initial coupling value beta0 : float - LO beta function + LO beta function coefficient beta1 : float - NLO beta function + NLO beta function coefficient beta2 : float - NNLO beta function + NNLO beta function coefficient Returns ------- @@ -308,11 +308,11 @@ def j02_exact(a1, a0, beta0, beta1, beta2): a0 : float initial coupling value beta0 : float - LO beta function + LO beta function coefficient beta1 : float - NLO beta function + NLO beta function coefficient beta2 : float - NNLO beta function + NNLO beta function coefficient Returns ------- @@ -343,7 +343,7 @@ def j22_expanded(a1, a0, beta0): a0 : float initial coupling value beta0 : float - LO beta function + LO beta function coefficient Returns ------- @@ -369,9 +369,9 @@ def j12_expanded(a1, a0, beta0, beta1): a0 : float initial coupling value beta0 : float - LO beta function + LO beta function coefficient beta1 : float - NLO beta function + NLO beta function coefficient Returns ------- @@ -398,11 +398,11 @@ def j02_expanded(a1, a0, beta0, beta1, beta2): a0 : float initial coupling value beta0 : float - LO beta function + LO beta function coefficient beta1 : float - NLO beta function + NLO beta function coefficient beta2 : float - NNLO beta function + NNLO beta function coefficient Returns ------- @@ -434,11 +434,11 @@ def jm12_exact(a1, a0, beta0, beta1, beta2): a0 : float initial coupling value beta0 : float - LO beta function + LO beta function coefficient beta1 : float - NLO beta function + NLO beta function coefficient beta2 : float - NNLO beta function + NNLO beta function coefficient Returns ------- From 55b495ac95d9db143432ce7e2f6ba4bbf7f39bb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 17 Jan 2023 13:38:55 +0100 Subject: [PATCH 274/312] Change definitions of evolution_integrals --- src/eko/kernels/as4_evolution_integrals.py | 60 +++++++++------- src/eko/kernels/evolution_integrals.py | 56 +++++++-------- src/eko/kernels/evolution_integrals_qcd.py | 46 ++++++------ src/eko/kernels/evolution_integrals_qed.py | 40 +++++------ src/eko/kernels/non_singlet.py | 28 ++++---- src/eko/kernels/non_singlet_qed.py | 36 +++++----- src/eko/kernels/singlet.py | 26 +++---- tests/eko/kernels/test_as4_ei.py | 10 +-- tests/eko/kernels/test_ei.py | 82 +++++++++++----------- 9 files changed, 197 insertions(+), 187 deletions(-) diff --git a/src/eko/kernels/as4_evolution_integrals.py b/src/eko/kernels/as4_evolution_integrals.py index d1e2a9e34..f401de8c6 100644 --- a/src/eko/kernels/as4_evolution_integrals.py +++ b/src/eko/kernels/as4_evolution_integrals.py @@ -1,4 +1,4 @@ -"""This file implements the |N3LO| evolution integrals""" +"""Implement the |N3LO| evolution integrals.""" import numba as nb import numpy as np @@ -6,7 +6,9 @@ @nb.njit(cache=True) def roots(b_list): - """Returns the roots of: + """Return the roots of a third grade polynomial. + + Return the roots of: .. math :: 1 + b_1 a_s + b_2 a_s^2 + b_3 a_s^3 = 0 @@ -41,7 +43,7 @@ def roots(b_list): @nb.njit(cache=True) def derivative(r, b_list): - r"""Returns the derivative: + r"""Return the derivative of a third grade polynomial. .. math :: \frac{d}{d a_s}(1 + b_1 a_s + b_2 a_s^2 + b_3 a_s^3) = b_1 + 2 b_2 r + 3 b_3 r^2 @@ -65,8 +67,9 @@ def derivative(r, b_list): @nb.njit(cache=True) def j33_exact(a1, a0, beta0, b_list, roots): - r"""|N3LO|-|N3LO| exact evolution definite integral - evaluated at :math:`a_s-a_s^0`. + r"""|N3LO|-|N3LO| exact evolution definite integral. + + Evaluated at :math:`a_s-a_s^0`. .. math:: @@ -99,8 +102,9 @@ def j33_exact(a1, a0, beta0, b_list, roots): @nb.njit(cache=True) def j23_exact(a1, a0, beta0, b_list, roots): - r"""|NNLO|-|N3LO| exact evolution definite integral - evaluated at :math:`a_s-a_s^0`. + r"""|NNLO|-|N3LO| exact evolution definite integral. + + Evaluated at :math:`a_s-a_s^0`. .. math:: @@ -133,8 +137,9 @@ def j23_exact(a1, a0, beta0, b_list, roots): @nb.njit(cache=True) def j13_exact(a1, a0, beta0, b_list, roots): - r"""|NLO|-|N3LO| exact evolution definite integral - evaluated at :math:`a_s-a_s^0`. + r"""|NLO|-|N3LO| exact evolution definite integral. + + Evaluated at :math:`a_s-a_s^0`. .. math:: @@ -166,9 +171,10 @@ def j13_exact(a1, a0, beta0, b_list, roots): @nb.njit(cache=True) -def j03_exact(j00, j13, j23, j33, b_list): - r"""|LO|-|N3LO| exact evolution definite integral - evaluated at :math:`a_s-a_s^0`. +def j03_exact(j12, j13, j23, j33, b_list): + r"""|LO|-|N3LO| exact evolution definite integral. + + Evaluated at :math:`a_s-a_s^0`. .. math:: @@ -176,7 +182,7 @@ def j03_exact(j00, j13, j23, j33, b_list): Parameters ---------- - j00: float + j12: float |LO|-|LO| evolution integral j13: float |NLO|-|N3LO| evolution integral @@ -194,13 +200,14 @@ def j03_exact(j00, j13, j23, j33, b_list): """ b1, b2, b3 = b_list - return j00 - b1 * j13 - b2 * j23 - b3 * j33 + return j12 - b1 * j13 - b2 * j23 - b3 * j33 @nb.njit(cache=True) def j33_expanded(a1, a0, beta0): - r"""|N3LO|-|N3LO| expanded evolution definite integral - evaluated at :math:`a_s-a_s^0`. + r"""|N3LO|-|N3LO| expanded evolution definite integral. + + Evaluated at :math:`a_s-a_s^0`. .. math:: j^{(3,3)}_{exp}(a_s) = \frac{1}{3 \beta_0} a_s^3 @@ -225,8 +232,9 @@ def j33_expanded(a1, a0, beta0): @nb.njit(cache=True) def j23_expanded(a1, a0, beta0, b_list): - r"""|NNLO|-|N3LO| expanded evolution definite integral - evaluated at :math:`a_s-a_s^0`. + r"""|NNLO|-|N3LO| expanded evolution definite integral. + + Evaluated at :math:`a_s-a_s^0`. .. math:: j^{(2,3)}_{exp}(a_s) = \frac{1}{\beta_0} ( \frac{1}{2} a_s^2 - \frac{b_1}{3} as^3) @@ -254,8 +262,9 @@ def j23_expanded(a1, a0, beta0, b_list): @nb.njit(cache=True) def j13_expanded(a1, a0, beta0, b_list): - r"""|NLO|-|N3LO| expanded evolution definite integral - evaluated at :math:`a_s-a_s^0`. + r"""|NLO|-|N3LO| expanded evolution definite integral. + + Evaluated at :math:`a_s-a_s^0`. .. math:: j^{(1,3)}_{exp}(a_s) = \frac{1}{\beta_0} ( a_s - \frac{b_1}{2} a_s^2 + \frac{b_1^2-b_2}{3} as^3) @@ -286,16 +295,17 @@ def j13_expanded(a1, a0, beta0, b_list): @nb.njit(cache=True) -def j03_expanded(j00, j13, j23, j33, b_list): - r"""|LO|-|N3LO| expanded evolution definite integral - evaluated at :math:`a_s-a_s^0`. +def j03_expanded(j12, j13, j23, j33, b_list): + r"""|LO|-|N3LO| expanded evolution definite integral. + + Evaluated at :math:`a_s-a_s^0`. .. math:: j^{(0,3)}_{exp}(a_s) = j^{(0,0)} - b_1 j^{(1,3)}_{exp}(a_s) - b_2 j^{(2,3)}_{exp}(a_s) - b_3 j^{(3,3)}_{exp}(a_s) Parameters ---------- - j00: float + j12: float |LO|-|LO| evolution integral j13: float |NLO|-|N3LO| expanded evolution integral @@ -316,4 +326,4 @@ def j03_expanded(j00, j13, j23, j33, b_list): j03_exact """ - return j03_exact(j00, j13, j23, j33, b_list) + return j03_exact(j12, j13, j23, j33, b_list) diff --git a/src/eko/kernels/evolution_integrals.py b/src/eko/kernels/evolution_integrals.py index a023904bc..9cbd6f921 100644 --- a/src/eko/kernels/evolution_integrals.py +++ b/src/eko/kernels/evolution_integrals.py @@ -3,7 +3,7 @@ Integrals needed for the exact evolutions are given by: .. math:: - j^{(n,m)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s}\!da_s'\,\frac{(a_s')^{1+n}}{-\beta^{(m)}(a_s')} + j^{(n,m)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s}\!da_s'\,\frac{(a_s')^{n}}{-\sum_{i=2}^{m} \beta^{(i)} a_s'^i} The expanded integrals are obtained from the exact results by Taylor expanding in the limit :math:`a_s,a_s^{0} \to 0` until :math:`\mathcal{O}( a_s^{m+1})` for :math:`N^{m}LO` computations. @@ -14,7 +14,7 @@ @nb.njit(cache=True) -def jm10(a1, a0, beta0): +def j02(a1, a0, beta0): r"""LO-LO QED exact evolution integral. .. math:: @@ -32,14 +32,14 @@ def jm10(a1, a0, beta0): Returns ------- - jm10 : float + j02 : float integral """ return (1.0 / a0 - 1.0 / a1) / beta0 @nb.njit(cache=True) -def j00(a1, a0, beta0): +def j12(a1, a0, beta0): r""" LO-LO exact evolution integral. @@ -58,14 +58,14 @@ def j00(a1, a0, beta0): Returns ------- - j00 : float + j12 : float integral """ return np.log(a1 / a0) / beta0 @nb.njit(cache=True) -def j11_exact(a1, a0, beta0, beta1): +def j23_exact(a1, a0, beta0, beta1): r""" NLO-NLO exact evolution integral. @@ -95,7 +95,7 @@ def j11_exact(a1, a0, beta0, beta1): @nb.njit(cache=True) -def j11_expanded(a1, a0, beta0): +def j23_expanded(a1, a0, beta0): r""" NLO-NLO expanded evolution integral. @@ -120,7 +120,7 @@ def j11_expanded(a1, a0, beta0): @nb.njit(cache=True) -def j01_exact(a1, a0, beta0, beta1): +def j13_exact(a1, a0, beta0, beta1): r""" LO-NLO exact evolution integral. @@ -146,11 +146,11 @@ def j01_exact(a1, a0, beta0, beta1): integral """ b1 = beta1 / beta0 - return j00(a1, a0, beta0) - b1 * j11_exact(a1, a0, beta0, beta1) + return j12(a1, a0, beta0) - b1 * j23_exact(a1, a0, beta0, beta1) @nb.njit(cache=True) -def j01_expanded(a1, a0, beta0, beta1): +def j13_expanded(a1, a0, beta0, beta1): r""" LO-NLO expanded evolution integral. @@ -174,11 +174,11 @@ def j01_expanded(a1, a0, beta0, beta1): integral """ b1 = beta1 / beta0 - return j00(a1, a0, beta0) - b1 * j11_expanded(a1, a0, beta0) + return j12(a1, a0, beta0) - b1 * j23_expanded(a1, a0, beta0) @nb.njit(cache=True) -def jm11_exact(a1, a0, beta0, beta1): +def j03_exact(a1, a0, beta0, beta1): r"""LO-NLO exact evolution integral. .. math:: @@ -208,7 +208,7 @@ def jm11_exact(a1, a0, beta0, beta1): @nb.njit(cache=True) -def j22_exact(a1, a0, beta0, beta1, beta2): +def j34_exact(a1, a0, beta0, beta1, beta2): r""" NNLO-NNLO exact evolution integral. @@ -253,7 +253,7 @@ def j22_exact(a1, a0, beta0, beta1, beta2): @nb.njit(cache=True) -def j12_exact(a1, a0, beta0, beta1, beta2): +def j24_exact(a1, a0, beta0, beta1, beta2): r""" NLO-NNLO exact evolution integral. @@ -292,7 +292,7 @@ def j12_exact(a1, a0, beta0, beta1, beta2): @nb.njit(cache=True) -def j02_exact(a1, a0, beta0, beta1, beta2): +def j14_exact(a1, a0, beta0, beta1, beta2): r""" LO-NNLO exact evolution integral. @@ -322,14 +322,14 @@ def j02_exact(a1, a0, beta0, beta1, beta2): b1 = beta1 / beta0 b2 = beta2 / beta0 return ( - j00(a1, a0, beta0) - - b1 * j12_exact(a1, a0, beta0, beta1, beta2) - - b2 * j22_exact(a1, a0, beta0, beta1, beta2) + j12(a1, a0, beta0) + - b1 * j24_exact(a1, a0, beta0, beta1, beta2) + - b2 * j34_exact(a1, a0, beta0, beta1, beta2) ) @nb.njit(cache=True) -def j22_expanded(a1, a0, beta0): +def j34_expanded(a1, a0, beta0): r""" NNLO-NNLO expanded evolution integral. @@ -354,7 +354,7 @@ def j22_expanded(a1, a0, beta0): @nb.njit(cache=True) -def j12_expanded(a1, a0, beta0, beta1): +def j24_expanded(a1, a0, beta0, beta1): r""" NLO-NNLO expanded evolution integral. @@ -383,7 +383,7 @@ def j12_expanded(a1, a0, beta0, beta1): @nb.njit(cache=True) -def j02_expanded(a1, a0, beta0, beta1, beta2): +def j14_expanded(a1, a0, beta0, beta1, beta2): r""" LO-NNLO expanded evolution integral. @@ -412,14 +412,14 @@ def j02_expanded(a1, a0, beta0, beta1, beta2): b1 = beta1 / beta0 b2 = beta2 / beta0 return ( - j00(a1, a0, beta0) - - b1 * j12_expanded(a1, a0, beta0, beta1) - - b2 * j22_expanded(a1, a0, beta0) + j12(a1, a0, beta0) + - b1 * j24_expanded(a1, a0, beta0, beta1) + - b2 * j34_expanded(a1, a0, beta0) ) @nb.njit(cache=True) -def jm12_exact(a1, a0, beta0, beta1, beta2): +def j04_exact(a1, a0, beta0, beta1, beta2): r"""LO-NNLO exact evolution integral. .. math:: @@ -448,7 +448,7 @@ def jm12_exact(a1, a0, beta0, beta1, beta2): b1 = beta1 / beta0 b2 = beta2 / beta0 return ( - jm10(a1, a0, beta0) - - b1 * j02_exact(a1, a0, beta0, beta1, beta2) - - b2 * j12_exact(a1, a0, beta0, beta1, beta2) + j02(a1, a0, beta0) + - b1 * j14_exact(a1, a0, beta0, beta1, beta2) + - b2 * j24_exact(a1, a0, beta0, beta1, beta2) ) diff --git a/src/eko/kernels/evolution_integrals_qcd.py b/src/eko/kernels/evolution_integrals_qcd.py index 07ecfbae4..36864ade8 100644 --- a/src/eko/kernels/evolution_integrals_qcd.py +++ b/src/eko/kernels/evolution_integrals_qcd.py @@ -6,7 +6,7 @@ @nb.njit(cache=True) -def j00(a1, a0, nf): +def j12(a1, a0, nf): r""" LO-LO exact evolution integral. @@ -25,15 +25,15 @@ def j00(a1, a0, nf): Returns ------- - j00 : float + j12 : float integral """ beta0 = beta.beta_qcd((2, 0), nf) - return ei.j00(a1, a0, beta0) + return ei.j12(a1, a0, beta0) @nb.njit(cache=True) -def j11_exact(a1, a0, nf): +def j23_exact(a1, a0, nf): r""" NLO-NLO exact evolution integral. @@ -58,11 +58,11 @@ def j11_exact(a1, a0, nf): """ beta0 = beta.beta_qcd((2, 0), nf) beta1 = beta.beta_qcd((3, 0), nf) - return ei.j11_exact(a1, a0, beta0, beta1) + return ei.j23_exact(a1, a0, beta0, beta1) @nb.njit(cache=True) -def j11_expanded(a1, a0, nf): +def j23_expanded(a1, a0, nf): r""" NLO-NLO expanded evolution integral. @@ -84,11 +84,11 @@ def j11_expanded(a1, a0, nf): integral """ beta0 = beta.beta_qcd((2, 0), nf) - return ei.j11_expanded(a1, a0, beta0) + return ei.j23_expanded(a1, a0, beta0) @nb.njit(cache=True) -def j01_exact(a1, a0, nf): +def j13_exact(a1, a0, nf): r""" LO-NLO exact evolution integral. @@ -113,11 +113,11 @@ def j01_exact(a1, a0, nf): """ beta0 = beta.beta_qcd((2, 0), nf) beta1 = beta.beta_qcd((3, 0), nf) - return ei.j01_exact(a1, a0, beta0, beta1) + return ei.j13_exact(a1, a0, beta0, beta1) @nb.njit(cache=True) -def j01_expanded(a1, a0, nf): +def j13_expanded(a1, a0, nf): r""" LO-NLO expanded evolution integral. @@ -140,11 +140,11 @@ def j01_expanded(a1, a0, nf): """ beta0 = beta.beta_qcd((2, 0), nf) beta1 = beta.beta_qcd((3, 0), nf) - return ei.j01_expanded(a1, a0, beta0, beta1) + return ei.j13_expanded(a1, a0, beta0, beta1) @nb.njit(cache=True) -def j22_exact(a1, a0, nf): +def j34_exact(a1, a0, nf): r""" NNLO-NNLO exact evolution integral. @@ -176,11 +176,11 @@ def j22_exact(a1, a0, nf): beta0 = beta.beta_qcd((2, 0), nf) beta1 = beta.beta_qcd((3, 0), nf) beta2 = beta.beta_qcd((4, 0), nf) - return ei.j22_exact(a1, a0, beta0, beta1, beta2) + return ei.j34_exact(a1, a0, beta0, beta1, beta2) @nb.njit(cache=True) -def j12_exact(a1, a0, nf): +def j24_exact(a1, a0, nf): r""" NLO-NNLO exact evolution integral. @@ -207,11 +207,11 @@ def j12_exact(a1, a0, nf): beta0 = beta.beta_qcd((2, 0), nf) beta1 = beta.beta_qcd((3, 0), nf) beta2 = beta.beta_qcd((4, 0), nf) - return ei.j12_exact(a1, a0, beta0, beta1, beta2) + return ei.j24_exact(a1, a0, beta0, beta1, beta2) @nb.njit(cache=True) -def j02_exact(a1, a0, nf): +def j14_exact(a1, a0, nf): r""" LO-NNLO exact evolution integral. @@ -237,11 +237,11 @@ def j02_exact(a1, a0, nf): beta0 = beta.beta_qcd((2, 0), nf) beta1 = beta.beta_qcd((3, 0), nf) beta2 = beta.beta_qcd((4, 0), nf) - return ei.j02_exact(a1, a0, beta0, beta1, beta2) + return ei.j14_exact(a1, a0, beta0, beta1, beta2) @nb.njit(cache=True) -def j22_expanded(a1, a0, nf): +def j34_expanded(a1, a0, nf): r""" NNLO-NNLO expanded evolution integral. @@ -263,11 +263,11 @@ def j22_expanded(a1, a0, nf): integral """ beta0 = beta.beta_qcd((2, 0), nf) - return ei.j22_expanded(a1, a0, beta0) + return ei.j34_expanded(a1, a0, beta0) @nb.njit(cache=True) -def j12_expanded(a1, a0, nf): +def j24_expanded(a1, a0, nf): r""" NLO-NNLO expanded evolution integral. @@ -292,11 +292,11 @@ def j12_expanded(a1, a0, nf): beta0 = beta.beta_qcd((2, 0), nf) beta1 = beta.beta_qcd((3, 0), nf) beta2 = beta.beta_qcd((4, 0), nf) - return ei.j12_exact(a1, a0, beta0, beta1, beta2) + return ei.j24_exact(a1, a0, beta0, beta1, beta2) @nb.njit(cache=True) -def j02_expanded(a1, a0, nf): +def j14_expanded(a1, a0, nf): r""" LO-NNLO expanded evolution integral. @@ -321,4 +321,4 @@ def j02_expanded(a1, a0, nf): beta0 = beta.beta_qcd((2, 0), nf) beta1 = beta.beta_qcd((3, 0), nf) beta2 = beta.beta_qcd((4, 0), nf) - return ei.j02_expanded(a1, a0, beta0, beta1, beta2) + return ei.j14_expanded(a1, a0, beta0, beta1, beta2) diff --git a/src/eko/kernels/evolution_integrals_qed.py b/src/eko/kernels/evolution_integrals_qed.py index 3f22f7945..0d2b1964a 100644 --- a/src/eko/kernels/evolution_integrals_qed.py +++ b/src/eko/kernels/evolution_integrals_qed.py @@ -6,7 +6,7 @@ @nb.njit(cache=True) -def j00(a1, a0, aem, nf): +def j12(a1, a0, aem, nf): r"""LO-LO QED exact evolution integral. .. math:: @@ -26,15 +26,15 @@ def j00(a1, a0, aem, nf): Returns ------- - j00 : float + j12 : float integral """ beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) - return ei.j00(a1, a0, beta0) + return ei.j12(a1, a0, beta0) @nb.njit(cache=True) -def jm10(a1, a0, aem, nf): +def j02(a1, a0, aem, nf): r"""LO-LO QED exact evolution integral. .. math:: @@ -54,15 +54,15 @@ def jm10(a1, a0, aem, nf): Returns ------- - j00 : float + j12 : float integral """ beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) - return ei.jm10(a1, a0, beta0) + return ei.j02(a1, a0, beta0) @nb.njit(cache=True) -def j11_exact(a1, a0, aem, nf): +def j23_exact(a1, a0, aem, nf): r"""NLO-NLO exact evolution integral. .. math:: @@ -88,11 +88,11 @@ def j11_exact(a1, a0, aem, nf): """ beta1 = beta.beta_qcd((3, 0), nf) beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) - return ei.j11_exact(a1, a0, beta0, beta1) + return ei.j23_exact(a1, a0, beta0, beta1) @nb.njit(cache=True) -def j01_exact(a1, a0, aem, nf): +def j13_exact(a1, a0, aem, nf): r"""LO-NLO QED exact evolution integral. .. math:: @@ -118,11 +118,11 @@ def j01_exact(a1, a0, aem, nf): """ beta1 = beta.beta_qcd((3, 0), nf) beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) - return ei.j01_exact(a1, a0, beta0, beta1) + return ei.j13_exact(a1, a0, beta0, beta1) @nb.njit(cache=True) -def jm11_exact(a1, a0, aem, nf): +def j03_exact(a1, a0, aem, nf): r"""LO-NLO exact evolution integral. .. math:: @@ -145,11 +145,11 @@ def jm11_exact(a1, a0, aem, nf): """ beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) beta1 = beta.beta_qcd((3, 0), nf) - return ei.jm11_exact(a1, a0, beta0, beta1) + return ei.j03_exact(a1, a0, beta0, beta1) @nb.njit(cache=True) -def j22_exact(a1, a0, aem, nf): +def j34_exact(a1, a0, aem, nf): r"""NNLO-NNLO exact evolution integral. .. math:: @@ -182,11 +182,11 @@ def j22_exact(a1, a0, aem, nf): beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) beta1 = beta.beta_qcd((3, 0), nf) beta2 = beta.beta_qcd((4, 0), nf) - return ei.j22_exact(a1, a0, beta0, beta1, beta2) + return ei.j34_exact(a1, a0, beta0, beta1, beta2) @nb.njit(cache=True) -def j12_exact(a1, a0, aem, nf): +def j24_exact(a1, a0, aem, nf): r"""NLO-NNLO exact evolution integral. .. math:: @@ -214,11 +214,11 @@ def j12_exact(a1, a0, aem, nf): beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) beta1 = beta.beta_qcd((3, 0), nf) beta2 = beta.beta_qcd((4, 0), nf) - return ei.j12_exact(a1, a0, beta0, beta1, beta2) + return ei.j24_exact(a1, a0, beta0, beta1, beta2) @nb.njit(cache=True) -def j02_exact(a1, a0, aem, nf): +def j14_exact(a1, a0, aem, nf): r"""LO-NNLO exact evolution integral. .. math:: @@ -245,11 +245,11 @@ def j02_exact(a1, a0, aem, nf): beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) beta1 = beta.beta_qcd((3, 0), nf) beta2 = beta.beta_qcd((4, 0), nf) - return ei.j02_exact(a1, a0, beta0, beta1, beta2) + return ei.j14_exact(a1, a0, beta0, beta1, beta2) @nb.njit(cache=True) -def jm12_exact(a1, a0, aem, nf): +def j04_exact(a1, a0, aem, nf): r"""LO-NNLO exact evolution integral. .. math:: @@ -274,4 +274,4 @@ def jm12_exact(a1, a0, aem, nf): beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) beta1 = beta.beta_qcd((3, 0), nf) beta2 = beta.beta_qcd((4, 0), nf) - return ei.jm12_exact(a1, a0, beta0, beta1, beta2) + return ei.j04_exact(a1, a0, beta0, beta1, beta2) diff --git a/src/eko/kernels/non_singlet.py b/src/eko/kernels/non_singlet.py index a8bd3acdf..0136b265c 100644 --- a/src/eko/kernels/non_singlet.py +++ b/src/eko/kernels/non_singlet.py @@ -67,7 +67,7 @@ def lo_exact(gamma_ns, a1, a0, nf): e_ns^0 : complex |LO| non-singlet exact EKO """ - return np.exp(gamma_ns[0] * ei.j00(a1, a0, nf)) + return np.exp(gamma_ns[0] * ei.j12(a1, a0, nf)) @nb.njit(cache=True) @@ -91,7 +91,7 @@ def nlo_exact(gamma_ns, a1, a0, nf): |NLO| non-singlet exact EKO """ return np.exp( - gamma_ns[0] * ei.j01_exact(a1, a0, nf) + gamma_ns[1] * ei.j11_exact(a1, a0, nf) + gamma_ns[0] * ei.j13_exact(a1, a0, nf) + gamma_ns[1] * ei.j23_exact(a1, a0, nf) ) @@ -116,8 +116,8 @@ def nlo_expanded(gamma_ns, a1, a0, nf): |NLO| non-singlet expanded EKO """ return np.exp( - gamma_ns[0] * ei.j01_expanded(a1, a0, nf) - + gamma_ns[1] * ei.j11_expanded(a1, a0, nf) + gamma_ns[0] * ei.j13_expanded(a1, a0, nf) + + gamma_ns[1] * ei.j23_expanded(a1, a0, nf) ) @@ -142,9 +142,9 @@ def nnlo_exact(gamma_ns, a1, a0, nf): |NNLO| non-singlet exact EKO """ return np.exp( - gamma_ns[0] * ei.j02_exact(a1, a0, nf) - + gamma_ns[1] * ei.j12_exact(a1, a0, nf) - + gamma_ns[2] * ei.j22_exact(a1, a0, nf) + gamma_ns[0] * ei.j14_exact(a1, a0, nf) + + gamma_ns[1] * ei.j24_exact(a1, a0, nf) + + gamma_ns[2] * ei.j34_exact(a1, a0, nf) ) @@ -169,9 +169,9 @@ def nnlo_expanded(gamma_ns, a1, a0, nf): |NNLO| non-singlet expanded EKO """ return np.exp( - gamma_ns[0] * ei.j02_expanded(a1, a0, nf) - + gamma_ns[1] * ei.j12_expanded(a1, a0, nf) - + gamma_ns[2] * ei.j22_expanded(a1, a0, nf) + gamma_ns[0] * ei.j14_expanded(a1, a0, nf) + + gamma_ns[1] * ei.j24_expanded(a1, a0, nf) + + gamma_ns[2] * ei.j34_expanded(a1, a0, nf) ) @@ -202,12 +202,12 @@ def n3lo_expanded(gamma_ns, a1, a0, nf): beta.b_qcd((4, 0), nf), beta.b_qcd((5, 0), nf), ] - j00 = ei.j00(a1, a0, nf) + j12 = ei.j12(a1, a0, nf) j13 = as4_ei.j13_expanded(a1, a0, beta0, b_list) j23 = as4_ei.j23_expanded(a1, a0, beta0, b_list) j33 = as4_ei.j33_expanded(a1, a0, beta0) return np.exp( - gamma_ns[0] * as4_ei.j03_expanded(j00, j13, j23, j33, b_list) + gamma_ns[0] * as4_ei.j03_expanded(j12, j13, j23, j33, b_list) + gamma_ns[1] * j13 + gamma_ns[2] * j23 + gamma_ns[3] * j33 @@ -242,12 +242,12 @@ def n3lo_exact(gamma_ns, a1, a0, nf): beta.b_qcd((5, 0), nf), ] roots = as4_ei.roots(b_list) - j00 = ei.j00(a1, a0, nf) + j12 = ei.j12(a1, a0, nf) j13 = as4_ei.j13_exact(a1, a0, beta0, b_list, roots) j23 = as4_ei.j23_exact(a1, a0, beta0, b_list, roots) j33 = as4_ei.j33_exact(a1, a0, beta0, b_list, roots) return np.exp( - gamma_ns[0] * as4_ei.j03_exact(j00, j13, j23, j33, b_list) + gamma_ns[0] * as4_ei.j03_exact(j12, j13, j23, j33, b_list) + gamma_ns[1] * j13 + gamma_ns[2] * j23 + gamma_ns[3] * j33 diff --git a/src/eko/kernels/non_singlet_qed.py b/src/eko/kernels/non_singlet_qed.py index 8331aeba3..3afa737e7 100644 --- a/src/eko/kernels/non_singlet_qed.py +++ b/src/eko/kernels/non_singlet_qed.py @@ -29,8 +29,8 @@ def as1aem1_exact(gamma_ns, a1, a0, aem, nf): O(as1aem1) non-singlet exact EKO """ return np.exp( - (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j00(a1, a0, aem, nf) - + aem * gamma_ns[0, 1] * ei.jm10(a1, a0, aem, nf) + (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j12(a1, a0, aem, nf) + + aem * gamma_ns[0, 1] * ei.j02(a1, a0, aem, nf) ) @@ -58,8 +58,8 @@ def as1aem2_exact(gamma_ns, a1, a0, aem, nf): O(as1aem2) non-singlet exact EKO """ return np.exp( - (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j00(a1, a0, aem, nf) - + (aem * gamma_ns[0, 1] + aem**2 * gamma_ns[0, 2]) * ei.jm10(a1, a0, aem, nf) + (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j12(a1, a0, aem, nf) + + (aem * gamma_ns[0, 1] + aem**2 * gamma_ns[0, 2]) * ei.j02(a1, a0, aem, nf) ) @@ -86,9 +86,9 @@ def as2aem1_exact(gamma_ns, a1, a0, aem, nf): O(as2aem1) non-singlet exact EKO """ return np.exp( - (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j01_exact(a1, a0, aem, nf) - + gamma_ns[2, 0] * ei.j11_exact(a1, a0, aem, nf) - + aem * gamma_ns[0, 1] * ei.jm11_exact(a1, a0, aem, nf) + (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j13_exact(a1, a0, aem, nf) + + gamma_ns[2, 0] * ei.j23_exact(a1, a0, aem, nf) + + aem * gamma_ns[0, 1] * ei.j03_exact(a1, a0, aem, nf) ) @@ -115,10 +115,10 @@ def as2aem2_exact(gamma_ns, a1, a0, aem, nf): O(as2aem2) non-singlet exact EKO """ return np.exp( - (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j01_exact(a1, a0, aem, nf) - + gamma_ns[2, 0] * ei.j11_exact(a1, a0, aem, nf) + (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j13_exact(a1, a0, aem, nf) + + gamma_ns[2, 0] * ei.j23_exact(a1, a0, aem, nf) + (aem * gamma_ns[0, 1] + aem**2 * gamma_ns[0, 2]) - * ei.jm11_exact(a1, a0, aem, nf) + * ei.j03_exact(a1, a0, aem, nf) ) @@ -145,10 +145,10 @@ def as3aem1_exact(gamma_ns, a1, a0, aem, nf): O(as3aem1) non-singlet exact EKO """ return np.exp( - (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j02_exact(a1, a0, aem, nf) - + gamma_ns[2, 0] * ei.j12_exact(a1, a0, aem, nf) - + gamma_ns[3, 0] * ei.j22_exact(a1, a0, aem, nf) - + aem * gamma_ns[0, 1] * ei.jm12_exact(a1, a0, aem, nf) + (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j14_exact(a1, a0, aem, nf) + + gamma_ns[2, 0] * ei.j24_exact(a1, a0, aem, nf) + + gamma_ns[3, 0] * ei.j34_exact(a1, a0, aem, nf) + + aem * gamma_ns[0, 1] * ei.j04_exact(a1, a0, aem, nf) ) @@ -176,11 +176,11 @@ def as3aem2_exact(gamma_ns, a1, a0, aem, nf): O(as3aem2) non-singlet exact EKO """ return np.exp( - (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j02_exact(a1, a0, aem, nf) - + gamma_ns[2, 0] * ei.j12_exact(a1, a0, aem, nf) - + gamma_ns[3, 0] * ei.j22_exact(a1, a0, aem, nf) + (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j14_exact(a1, a0, aem, nf) + + gamma_ns[2, 0] * ei.j24_exact(a1, a0, aem, nf) + + gamma_ns[3, 0] * ei.j34_exact(a1, a0, aem, nf) + (aem * gamma_ns[0, 1] + aem**2 * gamma_ns[0, 2]) - * ei.jm12_exact(a1, a0, aem, nf) + * ei.j04_exact(a1, a0, aem, nf) ) diff --git a/src/eko/kernels/singlet.py b/src/eko/kernels/singlet.py index 03eda0398..8d93a2eeb 100644 --- a/src/eko/kernels/singlet.py +++ b/src/eko/kernels/singlet.py @@ -30,7 +30,7 @@ def lo_exact(gamma_singlet, a1, a0, nf): numpy.ndarray singlet leading order exact EKO """ - return ad.exp_matrix_2D(gamma_singlet[0] * ei.j00(a1, a0, nf))[0] + return ad.exp_matrix_2D(gamma_singlet[0] * ei.j12(a1, a0, nf))[0] @nb.njit(cache=True) @@ -81,7 +81,7 @@ def nlo_decompose_exact(gamma_singlet, a1, a0, nf): singlet next-to-leading order decompose-exact EKO """ return nlo_decompose( - gamma_singlet, ei.j01_exact(a1, a0, nf), ei.j11_exact(a1, a0, nf) + gamma_singlet, ei.j13_exact(a1, a0, nf), ei.j23_exact(a1, a0, nf) ) @@ -106,7 +106,7 @@ def nlo_decompose_expanded(gamma_singlet, a1, a0, nf): singlet next-to-leading order decompose-expanded EKO """ return nlo_decompose( - gamma_singlet, ei.j01_expanded(a1, a0, nf), ei.j11_expanded(a1, a0, nf) + gamma_singlet, ei.j13_expanded(a1, a0, nf), ei.j23_expanded(a1, a0, nf) ) @@ -163,9 +163,9 @@ def nnlo_decompose_exact(gamma_singlet, a1, a0, nf): """ return nnlo_decompose( gamma_singlet, - ei.j02_exact(a1, a0, nf), - ei.j12_exact(a1, a0, nf), - ei.j22_exact(a1, a0, nf), + ei.j14_exact(a1, a0, nf), + ei.j24_exact(a1, a0, nf), + ei.j34_exact(a1, a0, nf), ) @@ -191,9 +191,9 @@ def nnlo_decompose_expanded(gamma_singlet, a1, a0, nf): """ return nnlo_decompose( gamma_singlet, - ei.j02_expanded(a1, a0, nf), - ei.j12_expanded(a1, a0, nf), - ei.j22_expanded(a1, a0, nf), + ei.j14_expanded(a1, a0, nf), + ei.j24_expanded(a1, a0, nf), + ei.j34_expanded(a1, a0, nf), ) @@ -260,12 +260,12 @@ def n3lo_decompose_exact(gamma_singlet, a1, a0, nf): beta.b_qcd((5, 0), nf), ] roots = as4_ei.roots(b_list) - j00 = ei.j00(a1, a0, nf) + j12 = ei.j12(a1, a0, nf) j13 = as4_ei.j13_exact(a1, a0, beta0, b_list, roots) j23 = as4_ei.j23_exact(a1, a0, beta0, b_list, roots) j33 = as4_ei.j33_exact(a1, a0, beta0, b_list, roots) return n3lo_decompose( - gamma_singlet, as4_ei.j03_exact(j00, j13, j23, j33, b_list), j13, j23, j33 + gamma_singlet, as4_ei.j03_exact(j12, j13, j23, j33, b_list), j13, j23, j33 ) @@ -295,12 +295,12 @@ def n3lo_decompose_expanded(gamma_singlet, a1, a0, nf): beta.b_qcd((4, 0), nf), beta.b_qcd((5, 0), nf), ] - j00 = ei.j00(a1, a0, nf) + j12 = ei.j12(a1, a0, nf) j13 = as4_ei.j13_expanded(a1, a0, beta0, b_list) j23 = as4_ei.j23_expanded(a1, a0, beta0, b_list) j33 = as4_ei.j33_expanded(a1, a0, beta0) return n3lo_decompose( - gamma_singlet, as4_ei.j03_expanded(j00, j13, j23, j33, b_list), j13, j23, j33 + gamma_singlet, as4_ei.j03_expanded(j12, j13, j23, j33, b_list), j13, j23, j33 ) diff --git a/tests/eko/kernels/test_as4_ei.py b/tests/eko/kernels/test_as4_ei.py index 3b0f0c74a..98ba546d9 100644 --- a/tests/eko/kernels/test_as4_ei.py +++ b/tests/eko/kernels/test_as4_ei.py @@ -2,7 +2,7 @@ from eko import beta from eko.kernels import as4_evolution_integrals as as4_ei -from eko.kernels.evolution_integrals_qcd import j00 +from eko.kernels.evolution_integrals_qcd import j12 def test_zero(): @@ -81,8 +81,8 @@ def test_der_n3lo_exa(): # 03 rhs = 1.0 / (a1 * den) - j00p = j00(a1 + 0.5 * delta_a, a0, nf) - j00m = j00(a1 - 0.5 * delta_a, a0, nf) + j00p = j12(a1 + 0.5 * delta_a, a0, nf) + j00m = j12(a1 - 0.5 * delta_a, a0, nf) lhs = ( as4_ei.j03_exact(j00p, j13p, j23p, j33p, b_list) - as4_ei.j03_exact(j00m, j13m, j23m, j33m, b_list) @@ -129,8 +129,8 @@ def test_der_n3lo_exp(): # 03 rhs = 1.0 / (a1 * den) - j00p = j00(a1 + 0.5 * delta_a, a0, nf) - j00m = j00(a1 - 0.5 * delta_a, a0, nf) + j00p = j12(a1 + 0.5 * delta_a, a0, nf) + j00m = j12(a1 - 0.5 * delta_a, a0, nf) lhs = ( as4_ei.j03_expanded(j00p, j13p, j23p, j33p, b_list) - as4_ei.j03_expanded(j00m, j13m, j23m, j33m, b_list) diff --git a/tests/eko/kernels/test_ei.py b/tests/eko/kernels/test_ei.py index f64bd8dcb..54d9d8544 100644 --- a/tests/eko/kernels/test_ei.py +++ b/tests/eko/kernels/test_ei.py @@ -9,17 +9,17 @@ def test_zero(): """No evolution results in exp(0)""" nf = 3 for fnc in [ - ei.j00, - ei.j01_exact, - ei.j01_expanded, - ei.j11_exact, - ei.j11_expanded, - ei.j02_exact, - ei.j02_expanded, - ei.j12_exact, - ei.j12_expanded, - ei.j22_exact, - ei.j22_expanded, + ei.j12, + ei.j13_exact, + ei.j13_expanded, + ei.j23_exact, + ei.j23_expanded, + ei.j14_exact, + ei.j14_expanded, + ei.j24_exact, + ei.j24_expanded, + ei.j34_exact, + ei.j34_expanded, ]: np.testing.assert_allclose(fnc(1, 1, nf), 0) @@ -28,15 +28,15 @@ def test_zero_qed(): """No evolution results in exp(0)""" nf = 3 for fnc in [ - ei_qed.j00, - ei_qed.jm10, - ei_qed.j11_exact, - ei_qed.j01_exact, - ei_qed.jm11_exact, - ei_qed.j22_exact, - ei_qed.j12_exact, - ei_qed.j02_exact, - ei_qed.jm12_exact, + ei_qed.j12, + ei_qed.j02, + ei_qed.j23_exact, + ei_qed.j13_exact, + ei_qed.j03_exact, + ei_qed.j34_exact, + ei_qed.j24_exact, + ei_qed.j14_exact, + ei_qed.j04_exact, ]: np.testing.assert_allclose(fnc(1, 1, 0.00058, nf), 0) @@ -49,7 +49,7 @@ def test_der_lo(): delta_a = -1e-6 rhs = 1.0 / (beta.beta_qcd((2, 0), nf) * a1) lhs = ( - ei.j00(a1 + 0.5 * delta_a, a0, nf) - ei.j00(a1 - 0.5 * delta_a, a0, nf) + ei.j12(a1 + 0.5 * delta_a, a0, nf) - ei.j12(a1 - 0.5 * delta_a, a0, nf) ) / delta_a np.testing.assert_allclose(rhs, lhs) @@ -63,8 +63,8 @@ def test_der_nlo_exp(): # 01 rhs = 1.0 / (beta.beta_qcd((2, 0), nf) * a1 + beta.beta_qcd((3, 0), nf) * a1**2) lhs = ( - ei.j01_expanded(a1 + 0.5 * delta_a, a0, nf) - - ei.j01_expanded(a1 - 0.5 * delta_a, a0, nf) + ei.j13_expanded(a1 + 0.5 * delta_a, a0, nf) + - ei.j13_expanded(a1 - 0.5 * delta_a, a0, nf) ) / delta_a np.testing.assert_allclose( rhs, lhs, atol=np.abs((beta.b_qcd((3, 0), nf) * a1) ** 2) @@ -72,8 +72,8 @@ def test_der_nlo_exp(): # 11 rhs = 1.0 / (beta.beta_qcd((2, 0), nf) + beta.beta_qcd((3, 0), nf) * a1) lhs = ( - ei.j11_expanded(a1 + 0.5 * delta_a, a0, nf) - - ei.j11_expanded(a1 - 0.5 * delta_a, a0, nf) + ei.j23_expanded(a1 + 0.5 * delta_a, a0, nf) + - ei.j23_expanded(a1 - 0.5 * delta_a, a0, nf) ) / delta_a np.testing.assert_allclose(rhs, lhs, atol=np.abs(beta.b_qcd((3, 0), nf) * a1)) @@ -87,15 +87,15 @@ def test_der_nlo_exa(): # 01 rhs = 1.0 / (beta.beta_qcd((2, 0), nf) * a1 + beta.beta_qcd((3, 0), nf) * a1**2) lhs = ( - ei.j01_exact(a1 + 0.5 * delta_a, a0, nf) - - ei.j01_exact(a1 - 0.5 * delta_a, a0, nf) + ei.j13_exact(a1 + 0.5 * delta_a, a0, nf) + - ei.j13_exact(a1 - 0.5 * delta_a, a0, nf) ) / delta_a np.testing.assert_allclose(rhs, lhs, atol=np.abs(delta_a)) # in fact O(delta_a^2) # 11 rhs = 1.0 / (beta.beta_qcd((2, 0), nf) + beta.beta_qcd((3, 0), nf) * a1) lhs = ( - ei.j11_exact(a1 + 0.5 * delta_a, a0, nf) - - ei.j11_exact(a1 - 0.5 * delta_a, a0, nf) + ei.j23_exact(a1 + 0.5 * delta_a, a0, nf) + - ei.j23_exact(a1 - 0.5 * delta_a, a0, nf) ) / delta_a np.testing.assert_allclose(rhs, lhs, atol=np.abs(delta_a)) # in fact O(delta_a^2) @@ -117,8 +117,8 @@ def test_der_nnlo_exp(): + beta.beta_qcd((4, 0), nf) * a1**3 ) lhs = ( - ei.j02_expanded(a1 + 0.5 * delta_a, a0, nf) - - ei.j02_expanded(a1 - 0.5 * delta_a, a0, nf) + ei.j14_expanded(a1 + 0.5 * delta_a, a0, nf) + - ei.j14_expanded(a1 - 0.5 * delta_a, a0, nf) ) / delta_a toll = ( ( @@ -136,8 +136,8 @@ def test_der_nnlo_exp(): + beta.beta_qcd((4, 0), nf) * a1**2 ) lhs = ( - ei.j12_expanded(a1 + 0.5 * delta_a, a0, nf) - - ei.j12_expanded(a1 - 0.5 * delta_a, a0, nf) + ei.j24_expanded(a1 + 0.5 * delta_a, a0, nf) + - ei.j24_expanded(a1 - 0.5 * delta_a, a0, nf) ) / delta_a toll = ( (beta.b_qcd((3, 0), nf) ** 2 - beta.b_qcd((4, 0), nf)) @@ -152,8 +152,8 @@ def test_der_nnlo_exp(): + beta.beta_qcd((4, 0), nf) * a1**2 ) lhs = ( - ei.j22_expanded(a1 + 0.5 * delta_a, a0, nf) - - ei.j22_expanded(a1 - 0.5 * delta_a, a0, nf) + ei.j34_expanded(a1 + 0.5 * delta_a, a0, nf) + - ei.j34_expanded(a1 - 0.5 * delta_a, a0, nf) ) / delta_a np.testing.assert_allclose( rhs, @@ -175,8 +175,8 @@ def test_der_nnlo_exa(): + beta.beta_qcd((4, 0), nf) * a1**3 ) lhs = ( - ei.j02_exact(a1 + 0.5 * delta_a, a0, nf) - - ei.j02_exact(a1 - 0.5 * delta_a, a0, nf) + ei.j14_exact(a1 + 0.5 * delta_a, a0, nf) + - ei.j14_exact(a1 - 0.5 * delta_a, a0, nf) ) / delta_a np.testing.assert_allclose(rhs, lhs, atol=np.abs(delta_a)) # in fact O(delta_a^2) # 12 @@ -186,8 +186,8 @@ def test_der_nnlo_exa(): + beta.beta_qcd((4, 0), nf) * a1**2 ) lhs = ( - ei.j12_exact(a1 + 0.5 * delta_a, a0, nf) - - ei.j12_exact(a1 - 0.5 * delta_a, a0, nf) + ei.j24_exact(a1 + 0.5 * delta_a, a0, nf) + - ei.j24_exact(a1 - 0.5 * delta_a, a0, nf) ) / delta_a np.testing.assert_allclose(rhs, lhs, atol=np.abs(delta_a)) # in fact O(delta_a^2) # 12 @@ -197,7 +197,7 @@ def test_der_nnlo_exa(): + beta.beta_qcd((4, 0), nf) * a1**2 ) lhs = ( - ei.j22_exact(a1 + 0.5 * delta_a, a0, nf) - - ei.j22_exact(a1 - 0.5 * delta_a, a0, nf) + ei.j34_exact(a1 + 0.5 * delta_a, a0, nf) + - ei.j34_exact(a1 - 0.5 * delta_a, a0, nf) ) / delta_a np.testing.assert_allclose(rhs, lhs, atol=np.abs(delta_a)) # in fact O(delta_a^2) From c30c7ac7dc671e9f28bff5336035327e17d797fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 17 Jan 2023 13:41:51 +0100 Subject: [PATCH 275/312] Fix docstrings --- src/eko/kernels/evolution_integrals.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/eko/kernels/evolution_integrals.py b/src/eko/kernels/evolution_integrals.py index 9cbd6f921..4673b9577 100644 --- a/src/eko/kernels/evolution_integrals.py +++ b/src/eko/kernels/evolution_integrals.py @@ -18,8 +18,8 @@ def j02(a1, a0, beta0): r"""LO-LO QED exact evolution integral. .. math:: - j^{(-1,0)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{(\beta_0 + aem \beta_{0,1}) a_s'^2} - = \frac{1.0 / a0 - 1.0 / as}{\beta_0 + aem \beta_{0,1}} + j^{(-1,0)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{\beta_0 a_s'^2} + = \frac{1.0 / a0 - 1.0 / as}{\beta_0} Parameters ---------- @@ -182,8 +182,8 @@ def j03_exact(a1, a0, beta0, beta1): r"""LO-NLO exact evolution integral. .. math:: - j^{(-1,1)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3} - = \frac{1.0 / a0 - 1.0 / as}{\beta_0 + aem \beta_{0,1}} + \frac{b_1}{(\beta_0 + aem \beta_{0,1}} \left(\log(1 + 1 / (as b_1)) - \log(1 + 1 / (a0 b_1)\right) + j^{(-1,1)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{\beta_0 a_s'^2 + \beta_1 a_s'^3} + = \frac{1.0 / a0 - 1.0 / as}{\beta_0 + \frac{b_1}{\beta_0} \left(\log(1 + 1 / (as b_1)) - \log(1 + 1 / (a0 b_1)\right) Parameters ---------- @@ -423,9 +423,9 @@ def j04_exact(a1, a0, beta0, beta1, beta2): r"""LO-NNLO exact evolution integral. .. math:: - j^{(-1,2)}(a_s,a_s^0,aem) &= \int\limits_{a_s^0}^{a_s}\!da_s'\, - \frac{1}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ - &= j^{(-1,0)}(a_s,a_s^0,aem) - b_1 j^{(0,2)}(a_s,a_s^0) - b_2 j^{(1,2)}(a_s,a_s^0) + j^{(-1,2)}(a_s,a_s^0) &= \int\limits_{a_s^0}^{a_s}\!da_s'\, + \frac{1}{\beta_0 a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ + &= j^{(-1,0)}(a_s,a_s^0) - b_1 j^{(0,2)}(a_s,a_s^0) - b_2 j^{(1,2)}(a_s,a_s^0) Parameters ---------- From 781a16f96432229e3e133530d058f42e4709dfad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 17 Jan 2023 13:49:05 +0100 Subject: [PATCH 276/312] Fix again docstrings --- src/eko/kernels/evolution_integrals.py | 56 +++++++++++++------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/eko/kernels/evolution_integrals.py b/src/eko/kernels/evolution_integrals.py index 4673b9577..597c35b6e 100644 --- a/src/eko/kernels/evolution_integrals.py +++ b/src/eko/kernels/evolution_integrals.py @@ -15,10 +15,10 @@ @nb.njit(cache=True) def j02(a1, a0, beta0): - r"""LO-LO QED exact evolution integral. + r"""j^{(0,2)} exact evolution integral. .. math:: - j^{(-1,0)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{\beta_0 a_s'^2} + j^{(0,2)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{\beta_0 a_s'^2} = \frac{1.0 / a0 - 1.0 / as}{\beta_0} Parameters @@ -41,10 +41,10 @@ def j02(a1, a0, beta0): @nb.njit(cache=True) def j12(a1, a0, beta0): r""" - LO-LO exact evolution integral. + j^{(1,2)} exact evolution integral. .. math:: - j^{(0,0)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{\beta_0 a_s'} + j^{(1,2)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{\beta_0 a_s'} = \frac{\ln(a_s/a_s^0)}{\beta_0} Parameters @@ -67,7 +67,7 @@ def j12(a1, a0, beta0): @nb.njit(cache=True) def j23_exact(a1, a0, beta0, beta1): r""" - NLO-NLO exact evolution integral. + j^{(2,3)} exact evolution integral. .. math:: j^{(1,1)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s}\!da_s'\, @@ -97,10 +97,10 @@ def j23_exact(a1, a0, beta0, beta1): @nb.njit(cache=True) def j23_expanded(a1, a0, beta0): r""" - NLO-NLO expanded evolution integral. + j^{(2,3)} expanded evolution integral. .. math:: - j^{(1,1)}_{exp}(a_s,a_s^0) = \frac 1 {\beta_0}(a_s - a_s^0) + j^{(2,3)}_{exp}(a_s,a_s^0) = \frac 1 {\beta_0}(a_s - a_s^0) Parameters ---------- @@ -122,12 +122,12 @@ def j23_expanded(a1, a0, beta0): @nb.njit(cache=True) def j13_exact(a1, a0, beta0, beta1): r""" - LO-NLO exact evolution integral. + j^{(1,3)} exact evolution integral. .. math:: - j^{(0,1)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s}\!da_s'\, + j^{(1,3)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s}\!da_s'\, \frac{a_s'}{\beta_0 a_s'^2 + \beta_1 a_s'^3} - = j^{(0,0)}(a_s,a_s^0) - b_1 j^{(1,1)}(a_s,a_s^0) + = j^{(0,0)}(a_s,a_s^0) - b_1 j^{(2,3)}(a_s,a_s^0) Parameters ---------- @@ -152,10 +152,10 @@ def j13_exact(a1, a0, beta0, beta1): @nb.njit(cache=True) def j13_expanded(a1, a0, beta0, beta1): r""" - LO-NLO expanded evolution integral. + j^{(1,3)} expanded evolution integral. .. math:: - j^{(0,1)}_{exp}(a_s,a_s^0) = j^{(0,0)}(a_s,a_s^0) - b_1 j^{(1,1)}_{exp}(a_s,a_s^0) + j^{(1,3)}_{exp}(a_s,a_s^0) = j^{(0,0)}(a_s,a_s^0) - b_1 j^{(2,3)}_{exp}(a_s,a_s^0) Parameters ---------- @@ -179,10 +179,10 @@ def j13_expanded(a1, a0, beta0, beta1): @nb.njit(cache=True) def j03_exact(a1, a0, beta0, beta1): - r"""LO-NLO exact evolution integral. + r"""j^{(0,3)} exact evolution integral. .. math:: - j^{(-1,1)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{\beta_0 a_s'^2 + \beta_1 a_s'^3} + j^{(0,3)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{\beta_0 a_s'^2 + \beta_1 a_s'^3} = \frac{1.0 / a0 - 1.0 / as}{\beta_0 + \frac{b_1}{\beta_0} \left(\log(1 + 1 / (as b_1)) - \log(1 + 1 / (a0 b_1)\right) Parameters @@ -210,10 +210,10 @@ def j03_exact(a1, a0, beta0, beta1): @nb.njit(cache=True) def j34_exact(a1, a0, beta0, beta1, beta2): r""" - NNLO-NNLO exact evolution integral. + j^{(3,4)} exact evolution integral. .. math:: - j^{(2,2)}(a_s,a_s^0) &= + j^{(3,4)}(a_s,a_s^0) &= \int\limits_{a_s^0}^{a_s}\!da_s'\,\frac{a_s'^3} {\beta_0 a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4} = \frac{1}{\beta_2}\ln\left( @@ -255,7 +255,7 @@ def j34_exact(a1, a0, beta0, beta1, beta2): @nb.njit(cache=True) def j24_exact(a1, a0, beta0, beta1, beta2): r""" - NLO-NNLO exact evolution integral. + j^{(2,4)} exact evolution integral. .. math:: j^{(1,2)}(a_s,a_s^0) &= \int\limits_{a_s^0}^{a_s}\!da_s'\,\frac{a_s'^2}{\beta_0 a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ @@ -294,12 +294,12 @@ def j24_exact(a1, a0, beta0, beta1, beta2): @nb.njit(cache=True) def j14_exact(a1, a0, beta0, beta1, beta2): r""" - LO-NNLO exact evolution integral. + j^{(1,4)} exact evolution integral. .. math:: j^{(0,2)}(a_s,a_s^0) &= \int\limits_{a_s^0}^{a_s}\!da_s'\, \frac{a_s'}{\beta_0 a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ - &= j^{(0,0)}(a_s,a_s^0) - b_1 j^{(1,2)}(a_s,a_s^0) - b_2 j^{(2,2)}(a_s,a_s^0) + &= j^{(0,0)}(a_s,a_s^0) - b_1 j^{(2,4)}(a_s,a_s^0) - b_2 j^{(3,4)}(a_s,a_s^0) Parameters ---------- @@ -331,7 +331,7 @@ def j14_exact(a1, a0, beta0, beta1, beta2): @nb.njit(cache=True) def j34_expanded(a1, a0, beta0): r""" - NNLO-NNLO expanded evolution integral. + j^{(3,4)} expanded evolution integral. .. math:: j^{(2,2)}_{exp}(a_s,a_s^0) = \frac{1}{2 \beta_0} \left( a_s^2 - (a_s^0)^{2} \right) @@ -356,10 +356,10 @@ def j34_expanded(a1, a0, beta0): @nb.njit(cache=True) def j24_expanded(a1, a0, beta0, beta1): r""" - NLO-NNLO expanded evolution integral. + j^{(2,4)} expanded evolution integral. .. math:: - j^{(1,2)}_{exp}(a_s,a_s^0) = \frac{1}{\beta_0}\left[ a_s - a_s^0 - + j^{(2,4)}_{exp}(a_s,a_s^0) = \frac{1}{\beta_0}\left[ a_s - a_s^0 - \frac{b_1}{2} \left( a_s^2 - (a_s^0)^{2} \right)\right] Parameters @@ -385,11 +385,11 @@ def j24_expanded(a1, a0, beta0, beta1): @nb.njit(cache=True) def j14_expanded(a1, a0, beta0, beta1, beta2): r""" - LO-NNLO expanded evolution integral. + j^{(1,4)} expanded evolution integral. .. math:: - j^{(0,2)}_{exp}(a_s,a_s^0) = j^{(0,0)}(a_s,a_s^0) - b_1 j^{(1,2)}_{exp}(a_s,a_s^0) - - b_2 j^{(2,2)}_{exp}(a_s,a_s^0) + j^{(1,4)}_{exp}(a_s,a_s^0) = j^{(0,0)}(a_s,a_s^0) - b_1 j^{(2,4)}_{exp}(a_s,a_s^0) + - b_2 j^{(3,4)}_{exp}(a_s,a_s^0) Parameters ---------- @@ -420,12 +420,12 @@ def j14_expanded(a1, a0, beta0, beta1, beta2): @nb.njit(cache=True) def j04_exact(a1, a0, beta0, beta1, beta2): - r"""LO-NNLO exact evolution integral. + r"""j^{(0,4)} exact evolution integral. .. math:: - j^{(-1,2)}(a_s,a_s^0) &= \int\limits_{a_s^0}^{a_s}\!da_s'\, + j^{(0,4)}(a_s,a_s^0) &= \int\limits_{a_s^0}^{a_s}\!da_s'\, \frac{1}{\beta_0 a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ - &= j^{(-1,0)}(a_s,a_s^0) - b_1 j^{(0,2)}(a_s,a_s^0) - b_2 j^{(1,2)}(a_s,a_s^0) + &= j^{(-1,0)}(a_s,a_s^0) - b_1 j^{(1,4)}(a_s,a_s^0) - b_2 j^{(2,4)}(a_s,a_s^0) Parameters ---------- From 610976afaffdc283bd4720a0765d136238bae6fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 17 Jan 2023 15:17:47 +0100 Subject: [PATCH 277/312] Upgrade notation in docstrings --- src/eko/kernels/evolution_integrals_qcd.py | 46 +++++++++++----------- src/eko/kernels/evolution_integrals_qed.py | 36 ++++++++--------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/src/eko/kernels/evolution_integrals_qcd.py b/src/eko/kernels/evolution_integrals_qcd.py index 36864ade8..fd66c2695 100644 --- a/src/eko/kernels/evolution_integrals_qcd.py +++ b/src/eko/kernels/evolution_integrals_qcd.py @@ -8,10 +8,10 @@ @nb.njit(cache=True) def j12(a1, a0, nf): r""" - LO-LO exact evolution integral. + j^{(1,2)} exact evolution integral. .. math:: - j^{(0,0)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{\beta_0 a_s'} + j^{(1,2)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{\beta_0 a_s'} = \frac{\ln(a_s/a_s^0)}{\beta_0} Parameters @@ -35,10 +35,10 @@ def j12(a1, a0, nf): @nb.njit(cache=True) def j23_exact(a1, a0, nf): r""" - NLO-NLO exact evolution integral. + j^{(2,3)} exact evolution integral. .. math:: - j^{(1,1)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s}\!da_s'\, + j^{(2,3)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s}\!da_s'\, \frac{a_s'^2}{\beta_0 a_s'^2 + \beta_1 a_s'^3} = \frac{1}{\beta_1}\ln\left(\frac{1+b_1 a_s}{1+b_1 a_s^0}\right) @@ -64,10 +64,10 @@ def j23_exact(a1, a0, nf): @nb.njit(cache=True) def j23_expanded(a1, a0, nf): r""" - NLO-NLO expanded evolution integral. + j^{(2,3)} expanded evolution integral. .. math:: - j^{(1,1)}_{exp}(a_s,a_s^0) = \frac 1 {\beta_0}(a_s - a_s^0) + j^{(2,3)}_{exp}(a_s,a_s^0) = \frac 1 {\beta_0}(a_s - a_s^0) Parameters ---------- @@ -90,10 +90,10 @@ def j23_expanded(a1, a0, nf): @nb.njit(cache=True) def j13_exact(a1, a0, nf): r""" - LO-NLO exact evolution integral. + j^{(1,3)} exact evolution integral. .. math:: - j^{(0,1)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s}\!da_s'\, + j^{(1,3)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s}\!da_s'\, \frac{a_s'}{\beta_0 a_s'^2 + \beta_1 a_s'^3} = j^{(0,0)}(a_s,a_s^0) - b_1 j^{(1,1)}(a_s,a_s^0) @@ -119,10 +119,10 @@ def j13_exact(a1, a0, nf): @nb.njit(cache=True) def j13_expanded(a1, a0, nf): r""" - LO-NLO expanded evolution integral. + j^{(1,3)} expanded evolution integral. .. math:: - j^{(0,1)}_{exp}(a_s,a_s^0) = j^{(0,0)}(a_s,a_s^0) - b_1 j^{(1,1)}_{exp}(a_s,a_s^0) + j^{(1,3)}_{exp}(a_s,a_s^0) = j^{(0,0)}(a_s,a_s^0) - b_1 j^{(1,1)}_{exp}(a_s,a_s^0) Parameters ---------- @@ -146,10 +146,10 @@ def j13_expanded(a1, a0, nf): @nb.njit(cache=True) def j34_exact(a1, a0, nf): r""" - NNLO-NNLO exact evolution integral. + j^{(3,4)} exact evolution integral. .. math:: - j^{(2,2)}(a_s,a_s^0) &= + j^{(3,4)}(a_s,a_s^0) &= \int\limits_{a_s^0}^{a_s}\!da_s'\,\frac{a_s'^3} {\beta_0 a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4} = \frac{1}{\beta_2}\ln\left( @@ -182,10 +182,10 @@ def j34_exact(a1, a0, nf): @nb.njit(cache=True) def j24_exact(a1, a0, nf): r""" - NLO-NNLO exact evolution integral. + j^{(2,4)} exact evolution integral. .. math:: - j^{(1,2)}(a_s,a_s^0) &= \int\limits_{a_s^0}^{a_s}\!da_s'\,\frac{a_s'^2}{\beta_0 a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ + j^{(2,4)}(a_s,a_s^0) &= \int\limits_{a_s^0}^{a_s}\!da_s'\,\frac{a_s'^2}{\beta_0 a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ &= \frac{2 \delta}{\beta_0 \Delta} \\ \delta &= \atan \left( \frac{b_1 + 2 a_s b_2 }{ \Delta} \right) - \atan \left( \frac{b_1 + 2 a_s^0 b_2 }{ \Delta} \right) \\ \Delta &= \sqrt{4 b_2 - b_1^2} @@ -213,10 +213,10 @@ def j24_exact(a1, a0, nf): @nb.njit(cache=True) def j14_exact(a1, a0, nf): r""" - LO-NNLO exact evolution integral. + j^{(1,4)} exact evolution integral. .. math:: - j^{(0,2)}(a_s,a_s^0) &= \int\limits_{a_s^0}^{a_s}\!da_s'\, + j^{(1,4)}(a_s,a_s^0) &= \int\limits_{a_s^0}^{a_s}\!da_s'\, \frac{a_s'}{\beta_0 a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ &= j^{(0,0)}(a_s,a_s^0) - b_1 j^{(1,2)}(a_s,a_s^0) - b_2 j^{(2,2)}(a_s,a_s^0) @@ -243,10 +243,10 @@ def j14_exact(a1, a0, nf): @nb.njit(cache=True) def j34_expanded(a1, a0, nf): r""" - NNLO-NNLO expanded evolution integral. + j^{(3,4)} expanded evolution integral. .. math:: - j^{(2,2)}_{exp}(a_s,a_s^0) = \frac{1}{2 \beta_0} \left( a_s^2 - (a_s^0)^{2} \right) + j^{(3,4)}_{exp}(a_s,a_s^0) = \frac{1}{2 \beta_0} \left( a_s^2 - (a_s^0)^{2} \right) Parameters ---------- @@ -269,10 +269,10 @@ def j34_expanded(a1, a0, nf): @nb.njit(cache=True) def j24_expanded(a1, a0, nf): r""" - NLO-NNLO expanded evolution integral. + j^{(2,4)} expanded evolution integral. .. math:: - j^{(1,2)}_{exp}(a_s,a_s^0) = \frac{1}{\beta_0}\left[ a_s - a_s^0 - + j^{(2,4)}_{exp}(a_s,a_s^0) = \frac{1}{\beta_0}\left[ a_s - a_s^0 - \frac{b_1}{2} \left( a_s^2 - (a_s^0)^{2} \right)\right] Parameters @@ -298,11 +298,11 @@ def j24_expanded(a1, a0, nf): @nb.njit(cache=True) def j14_expanded(a1, a0, nf): r""" - LO-NNLO expanded evolution integral. + j^{(1,4)} expanded evolution integral. .. math:: - j^{(0,2)}_{exp}(a_s,a_s^0) = j^{(0,0)}(a_s,a_s^0) - b_1 j^{(1,2)}_{exp}(a_s,a_s^0) - - b_2 j^{(2,2)}_{exp}(a_s,a_s^0) + j^{(1,4)}_{exp}(a_s,a_s^0) = j^{(0,0)}(a_s,a_s^0) - b_1 j^{(2,4)}_{exp}(a_s,a_s^0) + - b_2 j^{(3,4)}_{exp}(a_s,a_s^0) Parameters ---------- diff --git a/src/eko/kernels/evolution_integrals_qed.py b/src/eko/kernels/evolution_integrals_qed.py index 0d2b1964a..2e432ec5b 100644 --- a/src/eko/kernels/evolution_integrals_qed.py +++ b/src/eko/kernels/evolution_integrals_qed.py @@ -7,10 +7,10 @@ @nb.njit(cache=True) def j12(a1, a0, aem, nf): - r"""LO-LO QED exact evolution integral. + r"""j^{(1,2)} exact evolution integral with QED effects on beta0. .. math:: - j^{(0,0)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{(\beta_0 + aem \beta_{0,1}) a_s'} + j^{(1,2)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{(\beta_0 + aem \beta_{0,1}) a_s'} = \frac{\ln(a_s/a_s^0)}{\beta_0 + aem \beta_{0,1}} Parameters @@ -35,10 +35,10 @@ def j12(a1, a0, aem, nf): @nb.njit(cache=True) def j02(a1, a0, aem, nf): - r"""LO-LO QED exact evolution integral. + r"""j^{(0,2)} exact evolution integral with QED effects on beta0. .. math:: - j^{(-1,0)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{(\beta_0 + aem \beta_{0,1}) a_s'^2} + j^{(0,2)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{(\beta_0 + aem \beta_{0,1}) a_s'^2} = \frac{1.0 / a0 - 1.0 / as}{\beta_0 + aem \beta_{0,1}} Parameters @@ -63,10 +63,10 @@ def j02(a1, a0, aem, nf): @nb.njit(cache=True) def j23_exact(a1, a0, aem, nf): - r"""NLO-NLO exact evolution integral. + r"""j^{(2,3)} exact evolution integral with QED effects on beta0. .. math:: - j^{(1,1)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s}\!da_s'\, + j^{(2,3)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s}\!da_s'\, \frac{a_s'^2}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3} = \frac{1}{\beta_1}\ln\left(\frac{1+b_1 a_s}{1+b_1 a_s^0}\right) @@ -93,10 +93,10 @@ def j23_exact(a1, a0, aem, nf): @nb.njit(cache=True) def j13_exact(a1, a0, aem, nf): - r"""LO-NLO QED exact evolution integral. + r"""j^{(1,3)} exact evolution integral with QED effects on beta0. .. math:: - j^{(0,1)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s}\!da_s'\, + j^{(1,3)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s}\!da_s'\, \frac{a_s'}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3} = j^{(0,0)}(a_s,a_s^0,aem) - b_1 j^{(1,1)}(a_s,a_s^0,aem) @@ -123,10 +123,10 @@ def j13_exact(a1, a0, aem, nf): @nb.njit(cache=True) def j03_exact(a1, a0, aem, nf): - r"""LO-NLO exact evolution integral. + r"""j^{(0,3)} exact evolution integral with QED effects on beta0. .. math:: - j^{(-1,1)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3} + j^{(0,3)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3} = \frac{1.0 / a0 - 1.0 / as}{\beta_0 + aem \beta_{0,1}} + \frac{b_1}{(\beta_0 + aem \beta_{0,1}} \left(\log(1 + 1 / (as b_1)) - \log(1 + 1 / (a0 b_1)\right) Parameters @@ -150,10 +150,10 @@ def j03_exact(a1, a0, aem, nf): @nb.njit(cache=True) def j34_exact(a1, a0, aem, nf): - r"""NNLO-NNLO exact evolution integral. + r"""j^{(3,4)} exact evolution integral with QED effects on beta0. .. math:: - j^{(2,2)}(a_s,a_s^0,aem) &= + j^{(3,4)}(a_s,a_s^0,aem) &= \int\limits_{a_s^0}^{a_s}\!da_s'\,\frac{a_s'^3} {(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4} = \frac{1}{\beta_2}\ln\left( @@ -187,10 +187,10 @@ def j34_exact(a1, a0, aem, nf): @nb.njit(cache=True) def j24_exact(a1, a0, aem, nf): - r"""NLO-NNLO exact evolution integral. + r"""j^{(2,4)} exact evolution integral with QED effects on beta0. .. math:: - j^{(1,2)}(a_s,a_s^0,aem) &= \int\limits_{a_s^0}^{a_s}\!da_s'\,\frac{a_s'^2}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ + j^{(2,4)}(a_s,a_s^0,aem) &= \int\limits_{a_s^0}^{a_s}\!da_s'\,\frac{a_s'^2}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ &= \frac{2 \delta}{\beta_0 \Delta} \\ \delta &= \atan \left( \frac{b_1 + 2 a_s b_2 }{ \Delta} \right) - \atan \left( \frac{b_1 + 2 a_s^0 b_2 }{ \Delta} \right) \\ \Delta &= \sqrt{4 b_2 - b_1^2} @@ -219,10 +219,10 @@ def j24_exact(a1, a0, aem, nf): @nb.njit(cache=True) def j14_exact(a1, a0, aem, nf): - r"""LO-NNLO exact evolution integral. + r"""j^{(1,4)} exact evolution integral with QED effects on beta0. .. math:: - j^{(0,2)}(a_s,a_s^0,aem) &= \int\limits_{a_s^0}^{a_s}\!da_s'\, + j^{(1,4)}(a_s,a_s^0,aem) &= \int\limits_{a_s^0}^{a_s}\!da_s'\, \frac{a_s'}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ &= j^{(0,0)}(a_s,a_s^0) - b_1 j^{(1,2)}(a_s,a_s^0) - b_2 j^{(2,2)}(a_s,a_s^0) @@ -250,10 +250,10 @@ def j14_exact(a1, a0, aem, nf): @nb.njit(cache=True) def j04_exact(a1, a0, aem, nf): - r"""LO-NNLO exact evolution integral. + r"""j^{(0,4)} exact evolution integral with QED effects on beta0. .. math:: - j^{(-1,2)}(a_s,a_s^0,aem) &= \int\limits_{a_s^0}^{a_s}\!da_s'\, + j^{(0,4)}(a_s,a_s^0,aem) &= \int\limits_{a_s^0}^{a_s}\!da_s'\, \frac{1}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ &= j^{(-1,0)}(a_s,a_s^0,aem) - b_1 j^{(0,2)}(a_s,a_s^0) - b_2 j^{(1,2)}(a_s,a_s^0) From 7eeaafcdd701323a6a0a08e7fa0e109db1bd9ff0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 17 Jan 2023 16:22:25 +0100 Subject: [PATCH 278/312] Add math: ecc --- src/eko/kernels/evolution_integrals.py | 28 +++++++++++----------- src/eko/kernels/evolution_integrals_qcd.py | 22 ++++++++--------- src/eko/kernels/evolution_integrals_qed.py | 18 +++++++------- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/eko/kernels/evolution_integrals.py b/src/eko/kernels/evolution_integrals.py index 597c35b6e..2057ce694 100644 --- a/src/eko/kernels/evolution_integrals.py +++ b/src/eko/kernels/evolution_integrals.py @@ -15,7 +15,7 @@ @nb.njit(cache=True) def j02(a1, a0, beta0): - r"""j^{(0,2)} exact evolution integral. + r""":math:`j^{(0,2)}` exact evolution integral. .. math:: j^{(0,2)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{\beta_0 a_s'^2} @@ -41,7 +41,7 @@ def j02(a1, a0, beta0): @nb.njit(cache=True) def j12(a1, a0, beta0): r""" - j^{(1,2)} exact evolution integral. + :math:`j^{(1,2)}` exact evolution integral. .. math:: j^{(1,2)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{\beta_0 a_s'} @@ -67,7 +67,7 @@ def j12(a1, a0, beta0): @nb.njit(cache=True) def j23_exact(a1, a0, beta0, beta1): r""" - j^{(2,3)} exact evolution integral. + :math:`j^{(2,3)}` exact evolution integral. .. math:: j^{(1,1)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s}\!da_s'\, @@ -97,7 +97,7 @@ def j23_exact(a1, a0, beta0, beta1): @nb.njit(cache=True) def j23_expanded(a1, a0, beta0): r""" - j^{(2,3)} expanded evolution integral. + :math:`j^{(2,3)}` expanded evolution integral. .. math:: j^{(2,3)}_{exp}(a_s,a_s^0) = \frac 1 {\beta_0}(a_s - a_s^0) @@ -122,7 +122,7 @@ def j23_expanded(a1, a0, beta0): @nb.njit(cache=True) def j13_exact(a1, a0, beta0, beta1): r""" - j^{(1,3)} exact evolution integral. + :math:`j^{(1,3)}` exact evolution integral. .. math:: j^{(1,3)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s}\!da_s'\, @@ -152,7 +152,7 @@ def j13_exact(a1, a0, beta0, beta1): @nb.njit(cache=True) def j13_expanded(a1, a0, beta0, beta1): r""" - j^{(1,3)} expanded evolution integral. + :math:`j^{(1,3)}` expanded evolution integral. .. math:: j^{(1,3)}_{exp}(a_s,a_s^0) = j^{(0,0)}(a_s,a_s^0) - b_1 j^{(2,3)}_{exp}(a_s,a_s^0) @@ -179,7 +179,7 @@ def j13_expanded(a1, a0, beta0, beta1): @nb.njit(cache=True) def j03_exact(a1, a0, beta0, beta1): - r"""j^{(0,3)} exact evolution integral. + r""":math:`j^{(0,3)}` exact evolution integral. .. math:: j^{(0,3)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{\beta_0 a_s'^2 + \beta_1 a_s'^3} @@ -210,7 +210,7 @@ def j03_exact(a1, a0, beta0, beta1): @nb.njit(cache=True) def j34_exact(a1, a0, beta0, beta1, beta2): r""" - j^{(3,4)} exact evolution integral. + :math:`j^{(3,4)}` exact evolution integral. .. math:: j^{(3,4)}(a_s,a_s^0) &= @@ -255,7 +255,7 @@ def j34_exact(a1, a0, beta0, beta1, beta2): @nb.njit(cache=True) def j24_exact(a1, a0, beta0, beta1, beta2): r""" - j^{(2,4)} exact evolution integral. + :math:`j^{(2,4)}` exact evolution integral. .. math:: j^{(1,2)}(a_s,a_s^0) &= \int\limits_{a_s^0}^{a_s}\!da_s'\,\frac{a_s'^2}{\beta_0 a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ @@ -294,7 +294,7 @@ def j24_exact(a1, a0, beta0, beta1, beta2): @nb.njit(cache=True) def j14_exact(a1, a0, beta0, beta1, beta2): r""" - j^{(1,4)} exact evolution integral. + :math:`j^{(1,4)}` exact evolution integral. .. math:: j^{(0,2)}(a_s,a_s^0) &= \int\limits_{a_s^0}^{a_s}\!da_s'\, @@ -331,7 +331,7 @@ def j14_exact(a1, a0, beta0, beta1, beta2): @nb.njit(cache=True) def j34_expanded(a1, a0, beta0): r""" - j^{(3,4)} expanded evolution integral. + :math:`j^{(3,4)}` expanded evolution integral. .. math:: j^{(2,2)}_{exp}(a_s,a_s^0) = \frac{1}{2 \beta_0} \left( a_s^2 - (a_s^0)^{2} \right) @@ -356,7 +356,7 @@ def j34_expanded(a1, a0, beta0): @nb.njit(cache=True) def j24_expanded(a1, a0, beta0, beta1): r""" - j^{(2,4)} expanded evolution integral. + :math:`j^{(2,4)}` expanded evolution integral. .. math:: j^{(2,4)}_{exp}(a_s,a_s^0) = \frac{1}{\beta_0}\left[ a_s - a_s^0 - @@ -385,7 +385,7 @@ def j24_expanded(a1, a0, beta0, beta1): @nb.njit(cache=True) def j14_expanded(a1, a0, beta0, beta1, beta2): r""" - j^{(1,4)} expanded evolution integral. + :math:`j^{(1,4)}` expanded evolution integral. .. math:: j^{(1,4)}_{exp}(a_s,a_s^0) = j^{(0,0)}(a_s,a_s^0) - b_1 j^{(2,4)}_{exp}(a_s,a_s^0) @@ -420,7 +420,7 @@ def j14_expanded(a1, a0, beta0, beta1, beta2): @nb.njit(cache=True) def j04_exact(a1, a0, beta0, beta1, beta2): - r"""j^{(0,4)} exact evolution integral. + r""":math:`j^{(0,4)}` exact evolution integral. .. math:: j^{(0,4)}(a_s,a_s^0) &= \int\limits_{a_s^0}^{a_s}\!da_s'\, diff --git a/src/eko/kernels/evolution_integrals_qcd.py b/src/eko/kernels/evolution_integrals_qcd.py index fd66c2695..34ce96fe6 100644 --- a/src/eko/kernels/evolution_integrals_qcd.py +++ b/src/eko/kernels/evolution_integrals_qcd.py @@ -8,7 +8,7 @@ @nb.njit(cache=True) def j12(a1, a0, nf): r""" - j^{(1,2)} exact evolution integral. + :math:`j^{(1,2)}` exact evolution integral. .. math:: j^{(1,2)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{\beta_0 a_s'} @@ -35,7 +35,7 @@ def j12(a1, a0, nf): @nb.njit(cache=True) def j23_exact(a1, a0, nf): r""" - j^{(2,3)} exact evolution integral. + :math:`j^{(2,3)}` exact evolution integral. .. math:: j^{(2,3)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s}\!da_s'\, @@ -64,7 +64,7 @@ def j23_exact(a1, a0, nf): @nb.njit(cache=True) def j23_expanded(a1, a0, nf): r""" - j^{(2,3)} expanded evolution integral. + :math:`j^{(2,3)}` expanded evolution integral. .. math:: j^{(2,3)}_{exp}(a_s,a_s^0) = \frac 1 {\beta_0}(a_s - a_s^0) @@ -90,7 +90,7 @@ def j23_expanded(a1, a0, nf): @nb.njit(cache=True) def j13_exact(a1, a0, nf): r""" - j^{(1,3)} exact evolution integral. + :math:`j^{(1,3)}` exact evolution integral. .. math:: j^{(1,3)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s}\!da_s'\, @@ -119,7 +119,7 @@ def j13_exact(a1, a0, nf): @nb.njit(cache=True) def j13_expanded(a1, a0, nf): r""" - j^{(1,3)} expanded evolution integral. + :math:`j^{(1,3)}` expanded evolution integral. .. math:: j^{(1,3)}_{exp}(a_s,a_s^0) = j^{(0,0)}(a_s,a_s^0) - b_1 j^{(1,1)}_{exp}(a_s,a_s^0) @@ -146,7 +146,7 @@ def j13_expanded(a1, a0, nf): @nb.njit(cache=True) def j34_exact(a1, a0, nf): r""" - j^{(3,4)} exact evolution integral. + :math:`j^{(3,4)}` exact evolution integral. .. math:: j^{(3,4)}(a_s,a_s^0) &= @@ -182,7 +182,7 @@ def j34_exact(a1, a0, nf): @nb.njit(cache=True) def j24_exact(a1, a0, nf): r""" - j^{(2,4)} exact evolution integral. + :math:`j^{(2,4)}` exact evolution integral. .. math:: j^{(2,4)}(a_s,a_s^0) &= \int\limits_{a_s^0}^{a_s}\!da_s'\,\frac{a_s'^2}{\beta_0 a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ @@ -213,7 +213,7 @@ def j24_exact(a1, a0, nf): @nb.njit(cache=True) def j14_exact(a1, a0, nf): r""" - j^{(1,4)} exact evolution integral. + :math:`j^{(1,4)}` exact evolution integral. .. math:: j^{(1,4)}(a_s,a_s^0) &= \int\limits_{a_s^0}^{a_s}\!da_s'\, @@ -243,7 +243,7 @@ def j14_exact(a1, a0, nf): @nb.njit(cache=True) def j34_expanded(a1, a0, nf): r""" - j^{(3,4)} expanded evolution integral. + :math:`j^{(3,4)}` expanded evolution integral. .. math:: j^{(3,4)}_{exp}(a_s,a_s^0) = \frac{1}{2 \beta_0} \left( a_s^2 - (a_s^0)^{2} \right) @@ -269,7 +269,7 @@ def j34_expanded(a1, a0, nf): @nb.njit(cache=True) def j24_expanded(a1, a0, nf): r""" - j^{(2,4)} expanded evolution integral. + :math:`j^{(2,4)}` expanded evolution integral. .. math:: j^{(2,4)}_{exp}(a_s,a_s^0) = \frac{1}{\beta_0}\left[ a_s - a_s^0 - @@ -298,7 +298,7 @@ def j24_expanded(a1, a0, nf): @nb.njit(cache=True) def j14_expanded(a1, a0, nf): r""" - j^{(1,4)} expanded evolution integral. + :math:`j^{(1,4)}` expanded evolution integral. .. math:: j^{(1,4)}_{exp}(a_s,a_s^0) = j^{(0,0)}(a_s,a_s^0) - b_1 j^{(2,4)}_{exp}(a_s,a_s^0) diff --git a/src/eko/kernels/evolution_integrals_qed.py b/src/eko/kernels/evolution_integrals_qed.py index 2e432ec5b..729dac527 100644 --- a/src/eko/kernels/evolution_integrals_qed.py +++ b/src/eko/kernels/evolution_integrals_qed.py @@ -7,7 +7,7 @@ @nb.njit(cache=True) def j12(a1, a0, aem, nf): - r"""j^{(1,2)} exact evolution integral with QED effects on beta0. + r""":math:`j^{(1,2)}` exact evolution integral with QED effects on beta0. .. math:: j^{(1,2)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{(\beta_0 + aem \beta_{0,1}) a_s'} @@ -35,7 +35,7 @@ def j12(a1, a0, aem, nf): @nb.njit(cache=True) def j02(a1, a0, aem, nf): - r"""j^{(0,2)} exact evolution integral with QED effects on beta0. + r""":math:`j^{(0,2)}` exact evolution integral with QED effects on beta0. .. math:: j^{(0,2)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{(\beta_0 + aem \beta_{0,1}) a_s'^2} @@ -63,7 +63,7 @@ def j02(a1, a0, aem, nf): @nb.njit(cache=True) def j23_exact(a1, a0, aem, nf): - r"""j^{(2,3)} exact evolution integral with QED effects on beta0. + r""":math:`j^{(2,3)}` exact evolution integral with QED effects on beta0. .. math:: j^{(2,3)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s}\!da_s'\, @@ -93,7 +93,7 @@ def j23_exact(a1, a0, aem, nf): @nb.njit(cache=True) def j13_exact(a1, a0, aem, nf): - r"""j^{(1,3)} exact evolution integral with QED effects on beta0. + r""":math:`j^{(1,3)}` exact evolution integral with QED effects on beta0. .. math:: j^{(1,3)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s}\!da_s'\, @@ -123,7 +123,7 @@ def j13_exact(a1, a0, aem, nf): @nb.njit(cache=True) def j03_exact(a1, a0, aem, nf): - r"""j^{(0,3)} exact evolution integral with QED effects on beta0. + r""":math:`j^{(0,3)}` exact evolution integral with QED effects on beta0. .. math:: j^{(0,3)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3} @@ -150,7 +150,7 @@ def j03_exact(a1, a0, aem, nf): @nb.njit(cache=True) def j34_exact(a1, a0, aem, nf): - r"""j^{(3,4)} exact evolution integral with QED effects on beta0. + r""":math:`j^{(3,4)}` exact evolution integral with QED effects on beta0. .. math:: j^{(3,4)}(a_s,a_s^0,aem) &= @@ -187,7 +187,7 @@ def j34_exact(a1, a0, aem, nf): @nb.njit(cache=True) def j24_exact(a1, a0, aem, nf): - r"""j^{(2,4)} exact evolution integral with QED effects on beta0. + r""":math:`j^{(2,4)}` exact evolution integral with QED effects on beta0. .. math:: j^{(2,4)}(a_s,a_s^0,aem) &= \int\limits_{a_s^0}^{a_s}\!da_s'\,\frac{a_s'^2}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ @@ -219,7 +219,7 @@ def j24_exact(a1, a0, aem, nf): @nb.njit(cache=True) def j14_exact(a1, a0, aem, nf): - r"""j^{(1,4)} exact evolution integral with QED effects on beta0. + r""":math:`j^{(1,4)}` exact evolution integral with QED effects on beta0. .. math:: j^{(1,4)}(a_s,a_s^0,aem) &= \int\limits_{a_s^0}^{a_s}\!da_s'\, @@ -250,7 +250,7 @@ def j14_exact(a1, a0, aem, nf): @nb.njit(cache=True) def j04_exact(a1, a0, aem, nf): - r"""j^{(0,4)} exact evolution integral with QED effects on beta0. + r""":math:`j^{(0,4)}` exact evolution integral with QED effects on beta0. .. math:: j^{(0,4)}(a_s,a_s^0,aem) &= \int\limits_{a_s^0}^{a_s}\!da_s'\, From adf9059a058745e30e89e78d48dca342b1fce6e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 30 Jan 2023 10:25:27 +0100 Subject: [PATCH 279/312] Refactor tests in evvolution_operator/test_init --- tests/eko/evolution_operator/test_init.py | 260 +++++----------------- 1 file changed, 57 insertions(+), 203 deletions(-) diff --git a/tests/eko/evolution_operator/test_init.py b/tests/eko/evolution_operator/test_init.py index db9174c91..d7ac4a1cb 100644 --- a/tests/eko/evolution_operator/test_init.py +++ b/tests/eko/evolution_operator/test_init.py @@ -28,183 +28,40 @@ def test_quad_ker(monkeypatch): monkeypatch.setattr(ns, "dispatcher", lambda *args: 1.0) monkeypatch.setattr(qed_ns, "dispatcher", lambda *args: 1.0) monkeypatch.setattr(s, "dispatcher", lambda *args: np.identity(2)) - for is_log in [True, False]: - res_ns = quad_ker( - u=0, - order=(1, 0), - mode0=br.non_singlet_pids_map["ns+"], - mode1=0, - method="", - is_log=is_log, - logx=0.0, - areas=np.zeros(3), - as1=1, - as0=2, - as_raw=1, - aem_list=[0.00058], - alphaem_running=False, - nf=3, - L=0, - ev_op_iterations=0, - ev_op_max_order=(0, 0), - sv_mode=1, - is_threshold=False, - ) - np.testing.assert_allclose(res_ns, 0.0) - res_ns = quad_ker( - u=0, - order=(3, 1), - mode0=br.non_singlet_pids_map["ns+u"], - mode1=0, - method="", - is_log=is_log, - logx=0.0, - areas=np.zeros(3), - as1=1, - as0=2, - as_raw=1, - aem_list=[0.00058], - alphaem_running=False, - nf=3, - L=0, - ev_op_iterations=0, - ev_op_max_order=(0, 0), - sv_mode=1, - is_threshold=False, - ) - np.testing.assert_allclose(res_ns, 0.0) - res_s = quad_ker( - u=0, - order=(1, 0), - mode0=100, - mode1=100, - method="", - is_log=is_log, - logx=0.123, - areas=np.zeros(3), - as1=1, - as0=2, - as_raw=1, - aem_list=[0.00058], - alphaem_running=False, - nf=3, - L=0, - ev_op_iterations=0, - ev_op_max_order=(0, 0), - sv_mode=1, - is_threshold=False, - ) - np.testing.assert_allclose(res_s, 1.0) - res_s = quad_ker( - u=0, - order=(1, 1), - mode0=100, - mode1=100, - method="iterate-exact", - is_log=is_log, - logx=0.123, - areas=np.zeros(3), - as1=1, - as0=2, - as_raw=1, - aem_list=[0.00058], - alphaem_running=False, - nf=3, - L=0, - ev_op_iterations=0, - ev_op_max_order=(0, 0), - sv_mode=1, - is_threshold=False, - ) - np.testing.assert_allclose(res_s, 1.0) - res_s = quad_ker( - u=0, - order=(1, 0), - mode0=100, - mode1=21, - method="", - is_log=is_log, - logx=0.0, - areas=np.zeros(3), - as1=1, - as0=2, - as_raw=1, - aem_list=[0.00058], - alphaem_running=False, - nf=3, - L=0, - ev_op_iterations=0, - ev_op_max_order=(0, 0), - sv_mode=1, - is_threshold=False, - ) - np.testing.assert_allclose(res_s, 0.0) - res_s = quad_ker( - u=0, - order=(1, 1), - mode0=100, - mode1=21, - method="iterate-exact", - is_log=is_log, - logx=0.0, - areas=np.zeros(3), - as1=1, - as0=2, - as_raw=1, - aem_list=[0.00058], - alphaem_running=False, - nf=3, - L=0, - ev_op_iterations=0, - ev_op_max_order=(0, 0), - sv_mode=1, - is_threshold=False, - ) - np.testing.assert_allclose(res_s, 0.0) - res_v = quad_ker( - u=0, - order=(1, 1), - mode0=10200, - mode1=10200, - method="iterate-exact", - is_log=is_log, - logx=0.123, - areas=np.zeros(3), - as1=1, - as0=2, - as_raw=1, - aem_list=[0.00058], - alphaem_running=False, - nf=3, - L=0, - ev_op_iterations=0, - ev_op_max_order=(0, 0), - sv_mode=1, - is_threshold=False, - ) - np.testing.assert_allclose(res_v, 1.0) - res_v = quad_ker( - u=0, - order=(1, 1), - mode0=10200, - mode1=10204, - method="iterate-exact", - is_log=is_log, - logx=0.123, - areas=np.zeros(3), - as1=1, - as0=2, - as_raw=1, - aem_list=[0.00058], - alphaem_running=False, - nf=3, - L=0, - ev_op_iterations=0, - ev_op_max_order=(0, 0), - sv_mode=1, - is_threshold=False, - ) - np.testing.assert_allclose(res_v, 0.0) + params = [ + ((1, 0), br.non_singlet_pids_map["ns+"], 0, "", 0.0, 0.0), + ((3, 1), br.non_singlet_pids_map["ns+u"], 0, "", 0.0, 0.0), + ((1, 0), 100, 100, "", 0.123, 1.0), + ((1, 0), 100, 21, "", 0.0, 0.0), + ((1, 1), 100, 100, "iterate-exact", 0.123, 1.0), + ((1, 1), 100, 21, "iterate-exact", 0.123, 0.0), + ((1, 1), 10200, 10200, "iterate-exact", 0.123, 1.0), + ((1, 1), 10200, 10204, "iterate-exact", 0.123, 0.0), + ] + for (order, mode0, mode1, method, logx, res) in params: + for is_log in [True, False]: + res_ns = quad_ker( + u=0, + order=order, + mode0=mode0, + mode1=mode1, + method=method, + is_log=is_log, + logx=logx, + areas=np.zeros(3), + as1=1, + as0=2, + as_raw=1, + aem_list=[0.00058], + alphaem_running=False, + nf=3, + L=0, + ev_op_iterations=0, + ev_op_max_order=(0, 0), + sv_mode=1, + is_threshold=False, + ) + np.testing.assert_allclose(res_ns, res) for label in [(br.non_singlet_pids_map["ns+"], 0), (100, 100)]: for sv in [2, 3]: res_sv = quad_ker( @@ -287,14 +144,23 @@ def test_quad_ker(monkeypatch): np.testing.assert_allclose(res_ns, 0.0) +class FakeCoupling: + def __init__(self): + self.alphaem_running = None + self.q2_ref = 0.0 + + def a(self, scale_to=None, fact_scale=None, nf_to=None): + return (0.1, 0.01) + + def compute_aem_as(self, aem_ref, as_from, as_to, nf): + return aem_ref + + +fake_managers = {"couplings": FakeCoupling()} + + class TestOperator: - def test_labels(self, theory_ffns, operator_card, tmp_path): - tcard: TheoryCard = theory_ffns(3) - tcard.xif = 2.0 - ocard: OperatorCard = operator_card - ocard.configs.scvar_method = ScaleVariationsMethod.EXPONENTIATED - r = eko.runner.legacy.Runner(tcard, ocard, path=tmp_path / "eko.tar") - g = r.op_grid + def test_labels(self): o = Operator( dict( order=(3, 0), @@ -303,7 +169,7 @@ def test_labels(self, theory_ffns, operator_card, tmp_path): n_integration_cores=1, ModSV=None, ), - g.managers, + fake_managers, 3, 1, 2, @@ -317,20 +183,14 @@ def test_labels(self, theory_ffns, operator_card, tmp_path): n_integration_cores=1, ModSV=None, ), - g.managers, + fake_managers, 3, 1, 2, ) assert sorted(o.labels) == [] - def test_labels_qed(self, theory_ffns, operator_card, tmp_path): - tcard: TheoryCard = theory_ffns(3) - tcard.xif = 2.0 - ocard: OperatorCard = operator_card - ocard.configs.scvar_method = ScaleVariationsMethod.EXPONENTIATED - r = eko.runner.legacy.Runner(tcard, ocard, path=tmp_path / "eko.tar") - g = r.op_grid + def test_labels_qed(self): o = Operator( dict( order=(3, 1), @@ -340,7 +200,7 @@ def test_labels_qed(self, theory_ffns, operator_card, tmp_path): ModSV=None, ev_op_iterations=1, ), - g.managers, + fake_managers, 3, 1, 2, @@ -355,24 +215,18 @@ def test_labels_qed(self, theory_ffns, operator_card, tmp_path): ModSV=None, ev_op_iterations=1, ), - g.managers, + fake_managers, 3, 1, 2, ) assert sorted(o.labels) == [] - def test_n_pools(self, theory_ffns, operator_card, tmp_path): + def test_n_pools(self): excluded_cores = 3 # make sure we actually have more the those cores (e.g. on github we don't) if os.cpu_count() <= excluded_cores: return - tcard: TheoryCard = theory_ffns(3) - tcard.xif = 2.0 - ocard: OperatorCard = operator_card - ocard.configs.scvar_method = ScaleVariationsMethod.EXPONENTIATED - r = eko.runner.legacy.Runner(tcard, ocard, path=tmp_path / "eko.tar") - g = r.op_grid o = Operator( dict( order=(2, 0), @@ -381,7 +235,7 @@ def test_n_pools(self, theory_ffns, operator_card, tmp_path): n_integration_cores=-excluded_cores, ModSV=None, ), - g.managers, + fake_managers, 3, 1, 10, From 8d429960b50d0f9698fd53eb08100d0cc77b1caa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 2 Feb 2023 11:38:43 +0100 Subject: [PATCH 280/312] Change integration in QED sector for ns --- src/eko/evolution_operator/__init__.py | 10 ++ src/eko/kernels/non_singlet_qed.py | 108 +++++++++---- tests/eko/test_kernels_QEDns.py | 208 ++++++++++++++++--------- 3 files changed, 221 insertions(+), 105 deletions(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 0838e8e9e..1a0fc410a 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -193,6 +193,8 @@ def quad_ker( areas, as1, as0, + mu2_from, + mu2_to, as_raw, aem_list, alphaem_running, @@ -279,6 +281,8 @@ def quad_ker( method, as1, as0, + mu2_from, + mu2_to, as_raw, aem_list, alphaem_running, @@ -402,6 +406,8 @@ def quad_ker_qed( method, as1, as0, + mu2_from, + mu2_to, as_raw, aem_list, alphaem_running, @@ -523,6 +529,8 @@ def quad_ker_qed( alphaem_running, nf, ev_op_iterations, + mu2_from, + mu2_to, ) if sv_mode == sv.Modes.expanded and not is_threshold: ker = ( @@ -750,6 +758,8 @@ def quad_ker(self, label, logx, areas): areas=areas, as1=self.a_s[1], as0=self.a_s[0], + mu2_from=self.q2_from, + mu2_to=self.q2_to, as_raw=self.a_s[2], aem_list=self.aem_list_as, alphaem_running=self.alphaem_running, diff --git a/src/eko/kernels/non_singlet_qed.py b/src/eko/kernels/non_singlet_qed.py index 3afa737e7..9e13a8fe5 100644 --- a/src/eko/kernels/non_singlet_qed.py +++ b/src/eko/kernels/non_singlet_qed.py @@ -7,7 +7,7 @@ @nb.njit(cache=True) -def as1aem1_exact(gamma_ns, a1, a0, aem, nf): +def as1aem1_exact(gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from): """O(as1aem1) non-singlet exact EKO. Parameters @@ -30,12 +30,12 @@ def as1aem1_exact(gamma_ns, a1, a0, aem, nf): """ return np.exp( (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j12(a1, a0, aem, nf) - + aem * gamma_ns[0, 1] * ei.j02(a1, a0, aem, nf) + + aem * gamma_ns[0, 1] * np.log(mu2_from / mu2_to) ) @nb.njit(cache=True) -def as1aem2_exact(gamma_ns, a1, a0, aem, nf): +def as1aem2_exact(gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from): """ O(as1aem2) non-singlet exact EKO. @@ -59,12 +59,12 @@ def as1aem2_exact(gamma_ns, a1, a0, aem, nf): """ return np.exp( (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j12(a1, a0, aem, nf) - + (aem * gamma_ns[0, 1] + aem**2 * gamma_ns[0, 2]) * ei.j02(a1, a0, aem, nf) + + (aem * gamma_ns[0, 1] + aem**2 * gamma_ns[0, 2]) * np.log(mu2_from / mu2_to) ) @nb.njit(cache=True) -def as2aem1_exact(gamma_ns, a1, a0, aem, nf): +def as2aem1_exact(gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from): """O(as2aem1) non-singlet exact EKO. Parameters @@ -88,12 +88,12 @@ def as2aem1_exact(gamma_ns, a1, a0, aem, nf): return np.exp( (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j13_exact(a1, a0, aem, nf) + gamma_ns[2, 0] * ei.j23_exact(a1, a0, aem, nf) - + aem * gamma_ns[0, 1] * ei.j03_exact(a1, a0, aem, nf) + + aem * gamma_ns[0, 1] * np.log(mu2_from / mu2_to) ) @nb.njit(cache=True) -def as2aem2_exact(gamma_ns, a1, a0, aem, nf): +def as2aem2_exact(gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from): """O(as2aem2) non-singlet exact EKO. Parameters @@ -117,13 +117,12 @@ def as2aem2_exact(gamma_ns, a1, a0, aem, nf): return np.exp( (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j13_exact(a1, a0, aem, nf) + gamma_ns[2, 0] * ei.j23_exact(a1, a0, aem, nf) - + (aem * gamma_ns[0, 1] + aem**2 * gamma_ns[0, 2]) - * ei.j03_exact(a1, a0, aem, nf) + + (aem * gamma_ns[0, 1] + aem**2 * gamma_ns[0, 2]) * np.log(mu2_from / mu2_to) ) @nb.njit(cache=True) -def as3aem1_exact(gamma_ns, a1, a0, aem, nf): +def as3aem1_exact(gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from): """O(as3aem1) non-singlet exact EKO. Parameters @@ -148,12 +147,12 @@ def as3aem1_exact(gamma_ns, a1, a0, aem, nf): (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j14_exact(a1, a0, aem, nf) + gamma_ns[2, 0] * ei.j24_exact(a1, a0, aem, nf) + gamma_ns[3, 0] * ei.j34_exact(a1, a0, aem, nf) - + aem * gamma_ns[0, 1] * ei.j04_exact(a1, a0, aem, nf) + + aem * gamma_ns[0, 1] * np.log(mu2_from / mu2_to) ) @nb.njit(cache=True) -def as3aem2_exact(gamma_ns, a1, a0, aem, nf): +def as3aem2_exact(gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from): """ O(as3aem2) non-singlet exact EKO. @@ -179,14 +178,23 @@ def as3aem2_exact(gamma_ns, a1, a0, aem, nf): (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j14_exact(a1, a0, aem, nf) + gamma_ns[2, 0] * ei.j24_exact(a1, a0, aem, nf) + gamma_ns[3, 0] * ei.j34_exact(a1, a0, aem, nf) - + (aem * gamma_ns[0, 1] + aem**2 * gamma_ns[0, 2]) - * ei.j04_exact(a1, a0, aem, nf) + + (aem * gamma_ns[0, 1] + aem**2 * gamma_ns[0, 2]) * np.log(mu2_from / mu2_to) ) @nb.njit(cache=True) def dispatcher( - order, method, gamma_ns, a1, a0, aem_list, alphaem_running, nf, ev_op_iterations + order, + method, + gamma_ns, + a1, + a0, + aem_list, + alphaem_running, + nf, + ev_op_iterations, + mu2_to, + mu2_from, ): r"""Determine used kernel and call it. @@ -220,15 +228,15 @@ def dispatcher( """ if not alphaem_running: aem = aem_list[0] - return fixed_alphaem_exact(order, gamma_ns, a1, a0, aem, nf) + return fixed_alphaem_exact(order, gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from) else: return running_alphaem_exact( - order, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations + order, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations, mu2_to, mu2_from ) @nb.njit(cache=True) -def fixed_alphaem_exact(order, gamma_ns, a1, a0, aem, nf): +def fixed_alphaem_exact(order, gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from): """Compute exact solution for fixed alphaem. Parameters @@ -253,23 +261,25 @@ def fixed_alphaem_exact(order, gamma_ns, a1, a0, aem, nf): """ if order[1] == 1: if order[0] == 1: - return as1aem1_exact(gamma_ns, a1, a0, aem, nf) + return as1aem1_exact(gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from) if order[0] == 2: - return as2aem1_exact(gamma_ns, a1, a0, aem, nf) + return as2aem1_exact(gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from) if order[0] == 3: - return as3aem1_exact(gamma_ns, a1, a0, aem, nf) + return as3aem1_exact(gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from) if order[1] == 2: if order[0] == 1: - return as1aem2_exact(gamma_ns, a1, a0, aem, nf) + return as1aem2_exact(gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from) if order[0] == 2: - return as2aem2_exact(gamma_ns, a1, a0, aem, nf) + return as2aem2_exact(gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from) if order[0] == 3: - return as3aem2_exact(gamma_ns, a1, a0, aem, nf) + return as3aem2_exact(gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from) raise NotImplementedError("Selected order is not implemented") @nb.njit(cache=True) -def running_alphaem_exact(order, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations): +def running_alphaem_exact( + order, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations, mu2_to, mu2_from +): """Compute exact solution for running alphaem. Parameters @@ -302,38 +312,74 @@ def running_alphaem_exact(order, gamma_ns, a1, a0, aem_list, nf, ev_op_iteration if order[0] == 1: for step in range(1, ev_op_iterations + 1): res *= as1aem1_exact( - gamma_ns, a_steps[step], a_steps[step - 1], aem_list[step - 1], nf + gamma_ns, + a_steps[step], + a_steps[step - 1], + aem_list[step - 1], + nf, + mu2_to, + mu2_from, ) return res if order[0] == 2: for step in range(1, ev_op_iterations + 1): res *= as2aem1_exact( - gamma_ns, a_steps[step], a_steps[step - 1], aem_list[step - 1], nf + gamma_ns, + a_steps[step], + a_steps[step - 1], + aem_list[step - 1], + nf, + mu2_to, + mu2_from, ) return res if order[0] == 3: for step in range(1, ev_op_iterations + 1): res *= as3aem1_exact( - gamma_ns, a_steps[step], a_steps[step - 1], aem_list[step - 1], nf + gamma_ns, + a_steps[step], + a_steps[step - 1], + aem_list[step - 1], + nf, + mu2_to, + mu2_from, ) return res if order[1] == 2: if order[0] == 1: for step in range(1, ev_op_iterations + 1): res *= as1aem2_exact( - gamma_ns, a_steps[step], a_steps[step - 1], aem_list[step - 1], nf + gamma_ns, + a_steps[step], + a_steps[step - 1], + aem_list[step - 1], + nf, + mu2_to, + mu2_from, ) return res if order[0] == 2: for step in range(1, ev_op_iterations + 1): res *= as2aem2_exact( - gamma_ns, a_steps[step], a_steps[step - 1], aem_list[step - 1], nf + gamma_ns, + a_steps[step], + a_steps[step - 1], + aem_list[step - 1], + nf, + mu2_to, + mu2_from, ) return res if order[0] == 3: for step in range(1, ev_op_iterations + 1): res *= as3aem2_exact( - gamma_ns, a_steps[step], a_steps[step - 1], aem_list[step - 1], nf + gamma_ns, + a_steps[step], + a_steps[step - 1], + aem_list[step - 1], + nf, + mu2_to, + mu2_from, ) return res raise NotImplementedError("Selected order is not implemented") diff --git a/tests/eko/test_kernels_QEDns.py b/tests/eko/test_kernels_QEDns.py index 3a488e7fa..9ddce42d8 100644 --- a/tests/eko/test_kernels_QEDns.py +++ b/tests/eko/test_kernels_QEDns.py @@ -44,6 +44,8 @@ def test_zero(): running, nf, ev_op_iterations, + 1.0, + 1.0, ), 1.0, ) @@ -58,6 +60,8 @@ def test_zero(): running, nf, ev_op_iterations, + 1.0, + 1.0, ), 1.0, ) @@ -89,6 +93,8 @@ def test_zero_true_gamma(): running, nf, ev_op_iterations, + 1.0, + 1.0, ), 1.0, ) @@ -103,110 +109,156 @@ def test_zero_true_gamma(): running, nf, ev_op_iterations, + 1.0, + 1.0, ), 1.0, ) +from math import nan + +from eko.couplings import Couplings, couplings_mod_ev +from eko.io.types import ( + CouplingEvolutionMethod, + CouplingsRef, + EvolutionMethod, + MatchingScales, + QuarkMassSchemes, +) + +alpharef = (0.118, 0.00781) +masses = [m**2 for m in (2.0, 4.5, 175.0)] +muref = 91.0 +couplings = CouplingsRef.from_dict( + dict( + alphas=[alpharef[0], muref], + alphaem=[alpharef[1], nan], + num_flavs_ref=5, + max_num_flavs=6, + ) +) +evmod = CouplingEvolutionMethod.EXACT + + def test_ode(): - nf = 3 ev_op_iterations = 10 - aem_list = [0.01] * ev_op_iterations - delta_a = -1e-6 - a0 = 0.3 - betaQCD = np.zeros((4, 3), np.complex_) - for i in range(1, 3 + 1): - betaQCD[i, 0] = beta.beta_qcd((i + 1, 0), nf) - betaQCD[1, 1] = beta.beta_qcd((2, 1), nf) - for qcd in range(1, 3 + 1): - for qed in range(1, 2 + 1): - order = (qcd, qed) - for a1 in [0.1, 0.2]: - gamma_ns = ( - np.random.rand(3 + 1, 2 + 1) + np.random.rand(3 + 1, 2 + 1) * 1j + aem_list = [0.00781] * ev_op_iterations + nf = 5 + delta_mu2 = 1e-6 + mu2_0 = 5.0**2 + for mode in br.non_singlet_pids_map.values(): + if mode in [10201, 10101, 10200]: + continue + for qcd in range(1, 3 + 1): + for qed in range(1, 2 + 1): + order = (qcd, qed) + sc = Couplings( + couplings, + order, + evmod, + masses, + hqm_scheme=QuarkMassSchemes.POLE, + thresholds_ratios=MatchingScales(c=1.0, b=1.0, t=1.0), ) - gamma_ns[0, 0] = 0.0 - gamma_ns[2, 1] = 0.0 - gamma_ns[3, 1] = 0.0 - gamma_ns[1, 2] = 0.0 - gamma_ns[2, 2] = 0.0 - gamma_ns[3, 2] = 0.0 - gammatot = 0.0 - betatot = 0.0 - for i in range(0, order[0] + 1): - for j in range(0, order[1] + 1): - gammatot += gamma_ns[i, j] * a1**i * aem_list[0] ** j - betatot += a1**1 * betaQCD[i, j] * a1**i * aem_list[0] ** j - - r = gammatot / betatot - for method in methods: - rhs = r * ns.dispatcher( - order, - method, - gamma_ns, - a1, - a0, - aem_list, - False, - nf, - ev_op_iterations, + a0 = sc.a_s(mu2_0) + for mu2_to in [10**2, 15**2]: + dlog_mu2 = np.log(mu2_to + 0.5 * delta_mu2) - np.log( + mu2_to - 0.5 * delta_mu2 ) - lhs = ( - ns.dispatcher( - order, - method, - gamma_ns, - a1 + 0.5 * delta_a, - a0, - aem_list, - False, - nf, - ev_op_iterations, - ) - - ns.dispatcher( + a1 = sc.a_s(mu2_to) + gamma_ns = ( + np.random.rand(3 + 1, 2 + 1) + np.random.rand(3 + 1, 2 + 1) * 1j + ) + gamma_ns[0, 0] = 0.0 + gamma_ns[2, 1] = 0.0 + gamma_ns[3, 1] = 0.0 + gamma_ns[1, 2] = 0.0 + gamma_ns[2, 2] = 0.0 + gamma_ns[3, 2] = 0.0 + gammatot = 0.0 + for i in range(0, order[0] + 1): + for j in range(0, order[1] + 1): + gammatot += gamma_ns[i, j] * a1**i * aem_list[0] ** j + r = -gammatot + for method in methods: + rhs = r * ns.dispatcher( order, method, gamma_ns, - a1 - 0.5 * delta_a, + a1, a0, aem_list, False, nf, ev_op_iterations, + mu2_to, + mu2_0, ) - ) / delta_a - np.testing.assert_allclose(lhs, rhs, atol=np.abs(delta_a)) + lhs = ( + ns.dispatcher( + order, + method, + gamma_ns, + sc.a_s(mu2_to + 0.5 * delta_mu2), + a0, + aem_list, + False, + nf, + ev_op_iterations, + mu2_to + 0.5 * delta_mu2, + mu2_0, + ) + - ns.dispatcher( + order, + method, + gamma_ns, + sc.a_s(mu2_to - 0.5 * delta_mu2), + a0, + aem_list, + False, + nf, + ev_op_iterations, + mu2_to - 0.5 * delta_mu2, + mu2_0, + ) + ) / dlog_mu2 + np.testing.assert_allclose(lhs, rhs, atol=np.abs(1e-3)) def test_ode_true_gamma(): ev_op_iterations = 10 - aem_list = [0.01] * ev_op_iterations - nf = 3 - delta_a = -1e-6 - a0 = 0.3 - betaQCD = np.zeros((4, 3), np.complex_) - for i in range(1, 3 + 1): - betaQCD[i, 0] = beta.beta_qcd((i + 1, 0), nf) - betaQCD[1, 1] = beta.beta_qcd((2, 1), nf) + aem_list = [0.00781] * ev_op_iterations + nf = 5 + delta_mu2 = 1e-6 + mu2_0 = 5.0**2 for mode in br.non_singlet_pids_map.values(): if mode in [10201, 10101, 10200]: continue for qcd in range(1, 3 + 1): for qed in range(1, 2 + 1): order = (qcd, qed) - for a1 in [0.1, 0.2]: + sc = Couplings( + couplings, + order, + evmod, + masses, + hqm_scheme=QuarkMassSchemes.POLE, + thresholds_ratios=MatchingScales(c=1.0, b=1.0, t=1.0), + ) + a0 = sc.a_s(mu2_0) + for mu2_to in [10**2, 15**2]: + dlog_mu2 = np.log(mu2_to + 0.5 * delta_mu2) - np.log( + mu2_to - 0.5 * delta_mu2 + ) + a1 = sc.a_s(mu2_to) n = 3 + np.random.rand() gamma_ns = ad.gamma_ns_qed(order, mode, n, nf) gammatot = 0.0 - betatot = 0.0 for i in range(0, order[0] + 1): for j in range(0, order[1] + 1): gammatot += gamma_ns[i, j] * a1**i * aem_list[0] ** j - betatot += ( - a1**1 * betaQCD[i, j] * a1**i * aem_list[0] ** j - ) - - r = gammatot / betatot + r = -gammatot for method in methods: rhs = r * ns.dispatcher( order, @@ -218,32 +270,38 @@ def test_ode_true_gamma(): False, nf, ev_op_iterations, + mu2_to, + mu2_0, ) lhs = ( ns.dispatcher( order, method, gamma_ns, - a1 + 0.5 * delta_a, + sc.a_s(mu2_to + 0.5 * delta_mu2), a0, aem_list, False, nf, ev_op_iterations, + mu2_to + 0.5 * delta_mu2, + mu2_0, ) - ns.dispatcher( order, method, gamma_ns, - a1 - 0.5 * delta_a, + sc.a_s(mu2_to - 0.5 * delta_mu2), a0, aem_list, False, nf, ev_op_iterations, + mu2_to - 0.5 * delta_mu2, + mu2_0, ) - ) / delta_a - np.testing.assert_allclose(lhs, rhs, atol=np.abs(delta_a)) + ) / dlog_mu2 + np.testing.assert_allclose(lhs, rhs, atol=np.abs(1e-3)) def test_error(): @@ -259,4 +317,6 @@ def test_error(): running, 3, 10, + 1.0, + 1.0, ) From 8fc68ecdec2ef3b2a4e15372da745d4e54e20505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 2 Feb 2023 11:47:20 +0100 Subject: [PATCH 281/312] Add missing arguments --- tests/eko/evolution_operator/test_init.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/eko/evolution_operator/test_init.py b/tests/eko/evolution_operator/test_init.py index d7ac4a1cb..d07ff70f3 100644 --- a/tests/eko/evolution_operator/test_init.py +++ b/tests/eko/evolution_operator/test_init.py @@ -51,6 +51,8 @@ def test_quad_ker(monkeypatch): areas=np.zeros(3), as1=1, as0=2, + mu2_from=1, + mu2_to=2, as_raw=1, aem_list=[0.00058], alphaem_running=False, @@ -75,6 +77,8 @@ def test_quad_ker(monkeypatch): areas=np.zeros(3), as1=1, as0=2, + mu2_from=1, + mu2_to=2, as_raw=1, aem_list=[0.00058], alphaem_running=False, @@ -107,6 +111,8 @@ def test_quad_ker(monkeypatch): areas=np.zeros(3), as1=1, as0=2, + mu2_from=1, + mu2_to=2, as_raw=1, aem_list=[0.00058], alphaem_running=False, @@ -131,6 +137,8 @@ def test_quad_ker(monkeypatch): areas=np.zeros(3), as1=1, as0=2, + mu2_from=1, + mu2_to=2, as_raw=1, aem_list=[0.00058], alphaem_running=False, @@ -390,6 +398,8 @@ def quad_ker_pegasus( logxs = np.log(int_disp.xgrid.raw) as_raw = a1 = 1 a0 = 2 + mu2_from = 1 + mu2_to = 2**2 nf = 3 L = 0 ev_op_iterations = 10 @@ -409,6 +419,8 @@ def quad_ker_pegasus( bf.areas_representation, a1, a0, + mu2_from, + mu2_to, as_raw, [0.00058], False, From fdbe3ef1ec9eee3e031b17b535bc421c3e654128 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 7 Feb 2023 16:55:22 +0100 Subject: [PATCH 282/312] Refactor ns_qed kernels --- src/eko/kernels/evolution_integrals.py | 65 +++-- src/eko/kernels/evolution_integrals_qcd.py | 324 --------------------- src/eko/kernels/evolution_integrals_qed.py | 277 ------------------ src/eko/kernels/non_singlet.py | 85 +++--- src/eko/kernels/non_singlet_qed.py | 239 ++++----------- src/eko/kernels/singlet.py | 106 ++++--- tests/eko/kernels/test_as4_ei.py | 10 +- tests/eko/kernels/test_ei.py | 93 +++--- tests/eko/kernels/test_ns.py | 2 +- 9 files changed, 269 insertions(+), 932 deletions(-) delete mode 100644 src/eko/kernels/evolution_integrals_qcd.py delete mode 100644 src/eko/kernels/evolution_integrals_qed.py diff --git a/src/eko/kernels/evolution_integrals.py b/src/eko/kernels/evolution_integrals.py index 2057ce694..491fb2235 100644 --- a/src/eko/kernels/evolution_integrals.py +++ b/src/eko/kernels/evolution_integrals.py @@ -65,7 +65,7 @@ def j12(a1, a0, beta0): @nb.njit(cache=True) -def j23_exact(a1, a0, beta0, beta1): +def j23_exact(a1, a0, beta0, b_vec): r""" :math:`j^{(2,3)}` exact evolution integral. @@ -90,8 +90,8 @@ def j23_exact(a1, a0, beta0, beta1): j11 : float integral """ - b1 = beta1 / beta0 - return (1.0 / beta1) * np.log((1.0 + a1 * b1) / (1.0 + a0 * b1)) + b1 = b_vec[1] + return (1.0 / (b1 * beta0)) * np.log((1.0 + a1 * b1) / (1.0 + a0 * b1)) @nb.njit(cache=True) @@ -120,7 +120,7 @@ def j23_expanded(a1, a0, beta0): @nb.njit(cache=True) -def j13_exact(a1, a0, beta0, beta1): +def j13_exact(a1, a0, beta0, b_vec): r""" :math:`j^{(1,3)}` exact evolution integral. @@ -145,12 +145,12 @@ def j13_exact(a1, a0, beta0, beta1): j01 : float integral """ - b1 = beta1 / beta0 - return j12(a1, a0, beta0) - b1 * j23_exact(a1, a0, beta0, beta1) + b1 = b_vec[1] + return j12(a1, a0, beta0) - b1 * j23_exact(a1, a0, beta0, b_vec) @nb.njit(cache=True) -def j13_expanded(a1, a0, beta0, beta1): +def j13_expanded(a1, a0, beta0, b_vec): r""" :math:`j^{(1,3)}` expanded evolution integral. @@ -173,12 +173,12 @@ def j13_expanded(a1, a0, beta0, beta1): j01_exp : float integral """ - b1 = beta1 / beta0 + b1 = b_vec[1] return j12(a1, a0, beta0) - b1 * j23_expanded(a1, a0, beta0) @nb.njit(cache=True) -def j03_exact(a1, a0, beta0, beta1): +def j03_exact(a1, a0, beta0, b_vec): r""":math:`j^{(0,3)}` exact evolution integral. .. math:: @@ -201,14 +201,14 @@ def j03_exact(a1, a0, beta0, beta1): jm11 : float integral """ - b1 = beta1 / beta0 + b1 = b_vec[1] return -(1.0 / a1 - 1.0 / a0) / beta0 + b1 / beta0 * ( np.log(1.0 + 1.0 / (a1 * b1)) - np.log(1.0 + 1.0 / (a0 * b1)) ) @nb.njit(cache=True) -def j34_exact(a1, a0, beta0, beta1, beta2): +def j34_exact(a1, a0, beta0, b_vec): r""" :math:`j^{(3,4)}` exact evolution integral. @@ -241,8 +241,9 @@ def j34_exact(a1, a0, beta0, beta1, beta2): j22 : complex integral """ - b1 = beta1 / beta0 - b2 = beta2 / beta0 + b1 = b_vec[1] + b2 = b_vec[2] + beta2 = b2 * beta0 # allow Delta to be complex for nf = 6, the final result will be real Delta = np.sqrt(complex(4 * b2 - b1**2)) delta = np.arctan((b1 + 2 * a1 * b2) / Delta) - np.arctan( @@ -253,7 +254,7 @@ def j34_exact(a1, a0, beta0, beta1, beta2): @nb.njit(cache=True) -def j24_exact(a1, a0, beta0, beta1, beta2): +def j24_exact(a1, a0, beta0, b_vec): r""" :math:`j^{(2,4)}` exact evolution integral. @@ -281,8 +282,8 @@ def j24_exact(a1, a0, beta0, beta1, beta2): j12 : complex integral """ # pylint: disable=line-too-long - b1 = beta1 / beta0 - b2 = beta2 / beta0 + b1 = b_vec[1] + b2 = b_vec[2] # allow Delta to be complex for nf = 6, the final result will be real Delta = np.sqrt(complex(4 * b2 - b1**2)) delta = np.arctan((b1 + 2 * a1 * b2) / Delta) - np.arctan( @@ -292,7 +293,7 @@ def j24_exact(a1, a0, beta0, beta1, beta2): @nb.njit(cache=True) -def j14_exact(a1, a0, beta0, beta1, beta2): +def j14_exact(a1, a0, beta0, b_vec): r""" :math:`j^{(1,4)}` exact evolution integral. @@ -319,12 +320,12 @@ def j14_exact(a1, a0, beta0, beta1, beta2): j02 : complex integral """ - b1 = beta1 / beta0 - b2 = beta2 / beta0 + b1 = b_vec[1] + b2 = b_vec[2] return ( j12(a1, a0, beta0) - - b1 * j24_exact(a1, a0, beta0, beta1, beta2) - - b2 * j34_exact(a1, a0, beta0, beta1, beta2) + - b1 * j24_exact(a1, a0, beta0, b_vec) + - b2 * j34_exact(a1, a0, beta0, b_vec) ) @@ -354,7 +355,7 @@ def j34_expanded(a1, a0, beta0): @nb.njit(cache=True) -def j24_expanded(a1, a0, beta0, beta1): +def j24_expanded(a1, a0, beta0, b_vec): r""" :math:`j^{(2,4)}` expanded evolution integral. @@ -378,12 +379,12 @@ def j24_expanded(a1, a0, beta0, beta1): j12_exp : float integral """ - b1 = beta1 / beta0 + b1 = b_vec[1] return 1 / beta0 * (a1 - a0 - b1 / 2 * (a1**2 - a0**2)) @nb.njit(cache=True) -def j14_expanded(a1, a0, beta0, beta1, beta2): +def j14_expanded(a1, a0, beta0, b_vec): r""" :math:`j^{(1,4)}` expanded evolution integral. @@ -409,17 +410,17 @@ def j14_expanded(a1, a0, beta0, beta1, beta2): j02_exp : float integral """ - b1 = beta1 / beta0 - b2 = beta2 / beta0 + b1 = b_vec[1] + b2 = b_vec[2] return ( j12(a1, a0, beta0) - - b1 * j24_expanded(a1, a0, beta0, beta1) + - b1 * j24_expanded(a1, a0, beta0, b_vec) - b2 * j34_expanded(a1, a0, beta0) ) @nb.njit(cache=True) -def j04_exact(a1, a0, beta0, beta1, beta2): +def j04_exact(a1, a0, beta0, b_vec): r""":math:`j^{(0,4)}` exact evolution integral. .. math:: @@ -445,10 +446,10 @@ def j04_exact(a1, a0, beta0, beta1, beta2): jm12 : complex integral """ - b1 = beta1 / beta0 - b2 = beta2 / beta0 + b1 = b_vec[1] + b2 = b_vec[2] return ( j02(a1, a0, beta0) - - b1 * j14_exact(a1, a0, beta0, beta1, beta2) - - b2 * j24_exact(a1, a0, beta0, beta1, beta2) + - b1 * j14_exact(a1, a0, beta0, b_vec) + - b2 * j24_exact(a1, a0, beta0, b_vec) ) diff --git a/src/eko/kernels/evolution_integrals_qcd.py b/src/eko/kernels/evolution_integrals_qcd.py deleted file mode 100644 index 34ce96fe6..000000000 --- a/src/eko/kernels/evolution_integrals_qcd.py +++ /dev/null @@ -1,324 +0,0 @@ -r"""Compute evolution integrals needed for QED.""" -import numba as nb - -from .. import beta -from . import evolution_integrals as ei - - -@nb.njit(cache=True) -def j12(a1, a0, nf): - r""" - :math:`j^{(1,2)}` exact evolution integral. - - .. math:: - j^{(1,2)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{\beta_0 a_s'} - = \frac{\ln(a_s/a_s^0)}{\beta_0} - - Parameters - ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - nf : int - number of active flavors - - Returns - ------- - j12 : float - integral - """ - beta0 = beta.beta_qcd((2, 0), nf) - return ei.j12(a1, a0, beta0) - - -@nb.njit(cache=True) -def j23_exact(a1, a0, nf): - r""" - :math:`j^{(2,3)}` exact evolution integral. - - .. math:: - j^{(2,3)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s}\!da_s'\, - \frac{a_s'^2}{\beta_0 a_s'^2 + \beta_1 a_s'^3} - = \frac{1}{\beta_1}\ln\left(\frac{1+b_1 a_s}{1+b_1 a_s^0}\right) - - Parameters - ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - nf : int - number of active flavors - - Returns - ------- - j11 : float - integral - """ - beta0 = beta.beta_qcd((2, 0), nf) - beta1 = beta.beta_qcd((3, 0), nf) - return ei.j23_exact(a1, a0, beta0, beta1) - - -@nb.njit(cache=True) -def j23_expanded(a1, a0, nf): - r""" - :math:`j^{(2,3)}` expanded evolution integral. - - .. math:: - j^{(2,3)}_{exp}(a_s,a_s^0) = \frac 1 {\beta_0}(a_s - a_s^0) - - Parameters - ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - nf : int - number of active flavors - - Returns - ------- - j11_exp : float - integral - """ - beta0 = beta.beta_qcd((2, 0), nf) - return ei.j23_expanded(a1, a0, beta0) - - -@nb.njit(cache=True) -def j13_exact(a1, a0, nf): - r""" - :math:`j^{(1,3)}` exact evolution integral. - - .. math:: - j^{(1,3)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s}\!da_s'\, - \frac{a_s'}{\beta_0 a_s'^2 + \beta_1 a_s'^3} - = j^{(0,0)}(a_s,a_s^0) - b_1 j^{(1,1)}(a_s,a_s^0) - - Parameters - ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - nf : int - number of active flavors - - Returns - ------- - j11 : float - integral - """ - beta0 = beta.beta_qcd((2, 0), nf) - beta1 = beta.beta_qcd((3, 0), nf) - return ei.j13_exact(a1, a0, beta0, beta1) - - -@nb.njit(cache=True) -def j13_expanded(a1, a0, nf): - r""" - :math:`j^{(1,3)}` expanded evolution integral. - - .. math:: - j^{(1,3)}_{exp}(a_s,a_s^0) = j^{(0,0)}(a_s,a_s^0) - b_1 j^{(1,1)}_{exp}(a_s,a_s^0) - - Parameters - ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - nf : int - number of active flavors - - Returns - ------- - j01_exp : float - integral - """ - beta0 = beta.beta_qcd((2, 0), nf) - beta1 = beta.beta_qcd((3, 0), nf) - return ei.j13_expanded(a1, a0, beta0, beta1) - - -@nb.njit(cache=True) -def j34_exact(a1, a0, nf): - r""" - :math:`j^{(3,4)}` exact evolution integral. - - .. math:: - j^{(3,4)}(a_s,a_s^0) &= - \int\limits_{a_s^0}^{a_s}\!da_s'\,\frac{a_s'^3} - {\beta_0 a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4} - = \frac{1}{\beta_2}\ln\left( - \frac{1 + a_s ( b_1 + b_2 a_s ) }{ 1 + a_s^0 ( b_1 + b_2 a_s^0 )}\right) - - \frac{b_1 \delta}{ \beta_2 \Delta} \\ - \delta &= \atan \left( \frac{b_1 + 2 a_s b_2 }{ \Delta} \right) - - \atan \left( \frac{b_1 + 2 a_s^0 b_2 }{ \Delta} \right) \\ - \Delta &= \sqrt{4 b_2 - b_1^2} - - Parameters - ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - nf : int - number of active flavors - - Returns - ------- - j22 : complex - integral - """ - beta0 = beta.beta_qcd((2, 0), nf) - beta1 = beta.beta_qcd((3, 0), nf) - beta2 = beta.beta_qcd((4, 0), nf) - return ei.j34_exact(a1, a0, beta0, beta1, beta2) - - -@nb.njit(cache=True) -def j24_exact(a1, a0, nf): - r""" - :math:`j^{(2,4)}` exact evolution integral. - - .. math:: - j^{(2,4)}(a_s,a_s^0) &= \int\limits_{a_s^0}^{a_s}\!da_s'\,\frac{a_s'^2}{\beta_0 a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ - &= \frac{2 \delta}{\beta_0 \Delta} \\ - \delta &= \atan \left( \frac{b_1 + 2 a_s b_2 }{ \Delta} \right) - \atan \left( \frac{b_1 + 2 a_s^0 b_2 }{ \Delta} \right) \\ - \Delta &= \sqrt{4 b_2 - b_1^2} - - Parameters - ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - nf : int - number of active flavors - - Returns - ------- - j12 : complex - integral - """ # pylint: disable=line-too-long - beta0 = beta.beta_qcd((2, 0), nf) - beta1 = beta.beta_qcd((3, 0), nf) - beta2 = beta.beta_qcd((4, 0), nf) - return ei.j24_exact(a1, a0, beta0, beta1, beta2) - - -@nb.njit(cache=True) -def j14_exact(a1, a0, nf): - r""" - :math:`j^{(1,4)}` exact evolution integral. - - .. math:: - j^{(1,4)}(a_s,a_s^0) &= \int\limits_{a_s^0}^{a_s}\!da_s'\, - \frac{a_s'}{\beta_0 a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ - &= j^{(0,0)}(a_s,a_s^0) - b_1 j^{(1,2)}(a_s,a_s^0) - b_2 j^{(2,2)}(a_s,a_s^0) - - Parameters - ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - nf : int - number of active flavors - - Returns - ------- - j02 : complex - integral - """ - beta0 = beta.beta_qcd((2, 0), nf) - beta1 = beta.beta_qcd((3, 0), nf) - beta2 = beta.beta_qcd((4, 0), nf) - return ei.j14_exact(a1, a0, beta0, beta1, beta2) - - -@nb.njit(cache=True) -def j34_expanded(a1, a0, nf): - r""" - :math:`j^{(3,4)}` expanded evolution integral. - - .. math:: - j^{(3,4)}_{exp}(a_s,a_s^0) = \frac{1}{2 \beta_0} \left( a_s^2 - (a_s^0)^{2} \right) - - Parameters - ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - nf : int - number of active flavors - - Returns - ------- - j22_exp : float - integral - """ - beta0 = beta.beta_qcd((2, 0), nf) - return ei.j34_expanded(a1, a0, beta0) - - -@nb.njit(cache=True) -def j24_expanded(a1, a0, nf): - r""" - :math:`j^{(2,4)}` expanded evolution integral. - - .. math:: - j^{(2,4)}_{exp}(a_s,a_s^0) = \frac{1}{\beta_0}\left[ a_s - a_s^0 - - \frac{b_1}{2} \left( a_s^2 - (a_s^0)^{2} \right)\right] - - Parameters - ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - nf : int - number of active flavors - - Returns - ------- - j12_exp : float - integral - """ - beta0 = beta.beta_qcd((2, 0), nf) - beta1 = beta.beta_qcd((3, 0), nf) - beta2 = beta.beta_qcd((4, 0), nf) - return ei.j24_exact(a1, a0, beta0, beta1, beta2) - - -@nb.njit(cache=True) -def j14_expanded(a1, a0, nf): - r""" - :math:`j^{(1,4)}` expanded evolution integral. - - .. math:: - j^{(1,4)}_{exp}(a_s,a_s^0) = j^{(0,0)}(a_s,a_s^0) - b_1 j^{(2,4)}_{exp}(a_s,a_s^0) - - b_2 j^{(3,4)}_{exp}(a_s,a_s^0) - - Parameters - ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - nf : int - number of active flavors - - Returns - ------- - j02_exp : float - integral - """ - beta0 = beta.beta_qcd((2, 0), nf) - beta1 = beta.beta_qcd((3, 0), nf) - beta2 = beta.beta_qcd((4, 0), nf) - return ei.j14_expanded(a1, a0, beta0, beta1, beta2) diff --git a/src/eko/kernels/evolution_integrals_qed.py b/src/eko/kernels/evolution_integrals_qed.py deleted file mode 100644 index 729dac527..000000000 --- a/src/eko/kernels/evolution_integrals_qed.py +++ /dev/null @@ -1,277 +0,0 @@ -r"""Compute evolution integrals needed for QED.""" -import numba as nb - -from .. import beta -from . import evolution_integrals as ei - - -@nb.njit(cache=True) -def j12(a1, a0, aem, nf): - r""":math:`j^{(1,2)}` exact evolution integral with QED effects on beta0. - - .. math:: - j^{(1,2)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{(\beta_0 + aem \beta_{0,1}) a_s'} - = \frac{\ln(a_s/a_s^0)}{\beta_0 + aem \beta_{0,1}} - - Parameters - ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors - - Returns - ------- - j12 : float - integral - """ - beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) - return ei.j12(a1, a0, beta0) - - -@nb.njit(cache=True) -def j02(a1, a0, aem, nf): - r""":math:`j^{(0,2)}` exact evolution integral with QED effects on beta0. - - .. math:: - j^{(0,2)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{(\beta_0 + aem \beta_{0,1}) a_s'^2} - = \frac{1.0 / a0 - 1.0 / as}{\beta_0 + aem \beta_{0,1}} - - Parameters - ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors - - Returns - ------- - j12 : float - integral - """ - beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) - return ei.j02(a1, a0, beta0) - - -@nb.njit(cache=True) -def j23_exact(a1, a0, aem, nf): - r""":math:`j^{(2,3)}` exact evolution integral with QED effects on beta0. - - .. math:: - j^{(2,3)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s}\!da_s'\, - \frac{a_s'^2}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3} - = \frac{1}{\beta_1}\ln\left(\frac{1+b_1 a_s}{1+b_1 a_s^0}\right) - - Parameters - ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors - - Returns - ------- - j11 : float - integral - """ - beta1 = beta.beta_qcd((3, 0), nf) - beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) - return ei.j23_exact(a1, a0, beta0, beta1) - - -@nb.njit(cache=True) -def j13_exact(a1, a0, aem, nf): - r""":math:`j^{(1,3)}` exact evolution integral with QED effects on beta0. - - .. math:: - j^{(1,3)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s}\!da_s'\, - \frac{a_s'}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3} - = j^{(0,0)}(a_s,a_s^0,aem) - b_1 j^{(1,1)}(a_s,a_s^0,aem) - - Parameters - ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors - - Returns - ------- - j11 : float - integral - """ - beta1 = beta.beta_qcd((3, 0), nf) - beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) - return ei.j13_exact(a1, a0, beta0, beta1) - - -@nb.njit(cache=True) -def j03_exact(a1, a0, aem, nf): - r""":math:`j^{(0,3)}` exact evolution integral with QED effects on beta0. - - .. math:: - j^{(0,3)}(a_s,a_s^0,aem) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3} - = \frac{1.0 / a0 - 1.0 / as}{\beta_0 + aem \beta_{0,1}} + \frac{b_1}{(\beta_0 + aem \beta_{0,1}} \left(\log(1 + 1 / (as b_1)) - \log(1 + 1 / (a0 b_1)\right) - - Parameters - ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - nf : int - number of active flavors - - Returns - ------- - j11 : float - integral - """ - beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) - beta1 = beta.beta_qcd((3, 0), nf) - return ei.j03_exact(a1, a0, beta0, beta1) - - -@nb.njit(cache=True) -def j34_exact(a1, a0, aem, nf): - r""":math:`j^{(3,4)}` exact evolution integral with QED effects on beta0. - - .. math:: - j^{(3,4)}(a_s,a_s^0,aem) &= - \int\limits_{a_s^0}^{a_s}\!da_s'\,\frac{a_s'^3} - {(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4} - = \frac{1}{\beta_2}\ln\left( - \frac{1 + a_s ( b_1 + b_2 a_s ) }{ 1 + a_s^0 ( b_1 + b_2 a_s^0 )}\right) - - \frac{b_1 \delta}{ \beta_2 \Delta} \\ - \delta &= \atan \left( \frac{b_1 + 2 a_s b_2 }{ \Delta} \right) - - \atan \left( \frac{b_1 + 2 a_s^0 b_2 }{ \Delta} \right) \\ - \Delta &= \sqrt{4 b_2 - b_1^2} - - Parameters - ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors - - Returns - ------- - j22 : complex - integral - """ - beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) - beta1 = beta.beta_qcd((3, 0), nf) - beta2 = beta.beta_qcd((4, 0), nf) - return ei.j34_exact(a1, a0, beta0, beta1, beta2) - - -@nb.njit(cache=True) -def j24_exact(a1, a0, aem, nf): - r""":math:`j^{(2,4)}` exact evolution integral with QED effects on beta0. - - .. math:: - j^{(2,4)}(a_s,a_s^0,aem) &= \int\limits_{a_s^0}^{a_s}\!da_s'\,\frac{a_s'^2}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ - &= \frac{2 \delta}{\beta_0 \Delta} \\ - \delta &= \atan \left( \frac{b_1 + 2 a_s b_2 }{ \Delta} \right) - \atan \left( \frac{b_1 + 2 a_s^0 b_2 }{ \Delta} \right) \\ - \Delta &= \sqrt{4 b_2 - b_1^2} - - Parameters - ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors - - Returns - ------- - j12 : complex - integral - """ # pylint: disable=line-too-long - beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) - beta1 = beta.beta_qcd((3, 0), nf) - beta2 = beta.beta_qcd((4, 0), nf) - return ei.j24_exact(a1, a0, beta0, beta1, beta2) - - -@nb.njit(cache=True) -def j14_exact(a1, a0, aem, nf): - r""":math:`j^{(1,4)}` exact evolution integral with QED effects on beta0. - - .. math:: - j^{(1,4)}(a_s,a_s^0,aem) &= \int\limits_{a_s^0}^{a_s}\!da_s'\, - \frac{a_s'}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ - &= j^{(0,0)}(a_s,a_s^0) - b_1 j^{(1,2)}(a_s,a_s^0) - b_2 j^{(2,2)}(a_s,a_s^0) - - Parameters - ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors - - Returns - ------- - j02 : complex - integral - """ - beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) - beta1 = beta.beta_qcd((3, 0), nf) - beta2 = beta.beta_qcd((4, 0), nf) - return ei.j14_exact(a1, a0, beta0, beta1, beta2) - - -@nb.njit(cache=True) -def j04_exact(a1, a0, aem, nf): - r""":math:`j^{(0,4)}` exact evolution integral with QED effects on beta0. - - .. math:: - j^{(0,4)}(a_s,a_s^0,aem) &= \int\limits_{a_s^0}^{a_s}\!da_s'\, - \frac{1}{(\beta_0 + aem \beta_{0,1}) a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ - &= j^{(-1,0)}(a_s,a_s^0,aem) - b_1 j^{(0,2)}(a_s,a_s^0) - b_2 j^{(1,2)}(a_s,a_s^0) - - Parameters - ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - nf : int - number of active flavors - - Returns - ------- - j02 : complex - integral - """ - beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) - beta1 = beta.beta_qcd((3, 0), nf) - beta2 = beta.beta_qcd((4, 0), nf) - return ei.j04_exact(a1, a0, beta0, beta1, beta2) diff --git a/src/eko/kernels/non_singlet.py b/src/eko/kernels/non_singlet.py index 0136b265c..2b5d7c93f 100644 --- a/src/eko/kernels/non_singlet.py +++ b/src/eko/kernels/non_singlet.py @@ -5,12 +5,12 @@ from .. import beta from . import as4_evolution_integrals as as4_ei -from . import evolution_integrals_qcd as ei +from . import evolution_integrals as ei from . import utils @nb.njit(cache=True) -def U_vec(gamma_ns, nf, order): +def U_vec(gamma_ns, beta, order): r"""Compute the elements of the non-singlet U vector. Parameters @@ -29,26 +29,26 @@ def U_vec(gamma_ns, nf, order): """ U = np.zeros(order[0], dtype=np.complex_) - beta0 = beta.beta_qcd((2, 0), nf) + beta0 = beta[0] R0 = gamma_ns[0] / beta0 U[0] = 1.0 if order[0] >= 2: - b1 = beta.b_qcd((3, 0), nf) + b1 = beta[1] / beta[0] R1 = gamma_ns[1] / beta0 - b1 * R0 U[1] = R1 if order[0] >= 3: - b2 = beta.b_qcd((4, 0), nf) + b2 = beta[2] / beta[0] R2 = gamma_ns[2] / beta0 - b1 * R1 - b2 * R0 U[2] = 0.5 * (R2 + U[1] * R1) if order[0] >= 4: - b3 = beta.b_qcd((5, 0), nf) + b3 = beta[3] / beta[0] R3 = gamma_ns[3] / beta0 - b1 * R2 - b2 * R1 - b3 * R0 U[3] = 1 / 3 * (R3 + R2 * U[1] + R1 * U[2]) return U @nb.njit(cache=True) -def lo_exact(gamma_ns, a1, a0, nf): +def lo_exact(gamma_ns, a1, a0, beta): """|LO| non-singlet exact EKO. Parameters @@ -67,11 +67,12 @@ def lo_exact(gamma_ns, a1, a0, nf): e_ns^0 : complex |LO| non-singlet exact EKO """ - return np.exp(gamma_ns[0] * ei.j12(a1, a0, nf)) + beta0 = beta[0] + return np.exp(gamma_ns[0] * ei.j12(a1, a0, beta0)) @nb.njit(cache=True) -def nlo_exact(gamma_ns, a1, a0, nf): +def nlo_exact(gamma_ns, a1, a0, beta): """|NLO| non-singlet exact EKO. Parameters @@ -90,13 +91,16 @@ def nlo_exact(gamma_ns, a1, a0, nf): e_ns^1 : complex |NLO| non-singlet exact EKO """ + beta0 = beta[0] + b_vec = [betas / beta0 for betas in beta] return np.exp( - gamma_ns[0] * ei.j13_exact(a1, a0, nf) + gamma_ns[1] * ei.j23_exact(a1, a0, nf) + gamma_ns[0] * ei.j13_exact(a1, a0, beta0, b_vec) + + gamma_ns[1] * ei.j23_exact(a1, a0, beta0, b_vec) ) @nb.njit(cache=True) -def nlo_expanded(gamma_ns, a1, a0, nf): +def nlo_expanded(gamma_ns, a1, a0, beta): """|NLO| non-singlet expanded EKO. Parameters @@ -115,14 +119,16 @@ def nlo_expanded(gamma_ns, a1, a0, nf): e_ns^1 : complex |NLO| non-singlet expanded EKO """ + beta0 = beta[0] + b_vec = [betas / beta0 for betas in beta] return np.exp( - gamma_ns[0] * ei.j13_expanded(a1, a0, nf) - + gamma_ns[1] * ei.j23_expanded(a1, a0, nf) + gamma_ns[0] * ei.j13_expanded(a1, a0, beta0, b_vec) + + gamma_ns[1] * ei.j23_expanded(a1, a0, beta0) ) @nb.njit(cache=True) -def nnlo_exact(gamma_ns, a1, a0, nf): +def nnlo_exact(gamma_ns, a1, a0, beta): """|NNLO| non-singlet exact EKO. Parameters @@ -141,15 +147,17 @@ def nnlo_exact(gamma_ns, a1, a0, nf): e_ns^2 : complex |NNLO| non-singlet exact EKO """ + beta0 = beta[0] + b_vec = [betas / beta0 for betas in beta] return np.exp( - gamma_ns[0] * ei.j14_exact(a1, a0, nf) - + gamma_ns[1] * ei.j24_exact(a1, a0, nf) - + gamma_ns[2] * ei.j34_exact(a1, a0, nf) + gamma_ns[0] * ei.j14_exact(a1, a0, beta0, b_vec) + + gamma_ns[1] * ei.j24_exact(a1, a0, beta0, b_vec) + + gamma_ns[2] * ei.j34_exact(a1, a0, beta0, b_vec) ) @nb.njit(cache=True) -def nnlo_expanded(gamma_ns, a1, a0, nf): +def nnlo_expanded(gamma_ns, a1, a0, beta): """|NNLO| non-singlet expanded EKO. Parameters @@ -168,10 +176,12 @@ def nnlo_expanded(gamma_ns, a1, a0, nf): e_ns^2 : complex |NNLO| non-singlet expanded EKO """ + beta0 = beta[0] + b_vec = [betas / beta0 for betas in beta] return np.exp( - gamma_ns[0] * ei.j14_expanded(a1, a0, nf) - + gamma_ns[1] * ei.j24_expanded(a1, a0, nf) - + gamma_ns[2] * ei.j34_expanded(a1, a0, nf) + gamma_ns[0] * ei.j14_expanded(a1, a0, beta0, b_vec) + + gamma_ns[1] * ei.j24_expanded(a1, a0, beta0, b_vec) + + gamma_ns[2] * ei.j34_expanded(a1, a0, beta0) ) @@ -202,7 +212,7 @@ def n3lo_expanded(gamma_ns, a1, a0, nf): beta.b_qcd((4, 0), nf), beta.b_qcd((5, 0), nf), ] - j12 = ei.j12(a1, a0, nf) + j12 = ei.j12(a1, a0, beta0) j13 = as4_ei.j13_expanded(a1, a0, beta0, b_list) j23 = as4_ei.j23_expanded(a1, a0, beta0, b_list) j33 = as4_ei.j33_expanded(a1, a0, beta0) @@ -242,7 +252,7 @@ def n3lo_exact(gamma_ns, a1, a0, nf): beta.b_qcd((5, 0), nf), ] roots = as4_ei.roots(b_list) - j12 = ei.j12(a1, a0, nf) + j12 = ei.j12(a1, a0, beta0) j13 = as4_ei.j13_exact(a1, a0, beta0, b_list, roots) j23 = as4_ei.j23_exact(a1, a0, beta0, b_list, roots) j33 = as4_ei.j33_exact(a1, a0, beta0, b_list, roots) @@ -255,7 +265,7 @@ def n3lo_exact(gamma_ns, a1, a0, nf): @nb.njit(cache=True) -def eko_ordered_truncated(gamma_ns, a1, a0, nf, order, ev_op_iterations): +def eko_ordered_truncated(gamma_ns, a1, a0, beta, order, ev_op_iterations): """|NLO|, |NNLO| or |N3LO| non-singlet ordered truncated EKO. Parameters @@ -280,11 +290,11 @@ def eko_ordered_truncated(gamma_ns, a1, a0, nf, order, ev_op_iterations): """ a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) - U = U_vec(gamma_ns, nf, order) + U = U_vec(gamma_ns, beta, order) e = 1.0 al = a_steps[0] for ah in a_steps[1:]: - e0 = lo_exact(gamma_ns, ah, al, nf) + e0 = lo_exact(gamma_ns, ah, al, beta) num, den = 0, 0 for i in range(order[0]): num += U[i] * ah**i @@ -295,7 +305,7 @@ def eko_ordered_truncated(gamma_ns, a1, a0, nf, order, ev_op_iterations): @nb.njit(cache=True) -def eko_truncated(gamma_ns, a1, a0, nf, order, ev_op_iterations): +def eko_truncated(gamma_ns, a1, a0, beta, order, ev_op_iterations): """|NLO|, |NNLO| or |N3LO| non-singlet truncated EKO. Parameters @@ -320,12 +330,12 @@ def eko_truncated(gamma_ns, a1, a0, nf, order, ev_op_iterations): """ a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) - U = U_vec(gamma_ns, nf, order) + U = U_vec(gamma_ns, beta, order) e = 1.0 al = a_steps[0] fact = U[0] for ah in a_steps[1:]: - e0 = lo_exact(gamma_ns, ah, al, nf) + e0 = lo_exact(gamma_ns, ah, al, beta) if order[0] >= 2: fact += U[1] * (ah - al) if order[0] >= 3: @@ -373,13 +383,16 @@ def dispatcher( non-singlet EKO """ + betalist = [beta.beta_qcd((2 + i, 0), nf) for i in range(order[0])] # use always exact in LO if order[0] == 1: - return lo_exact(gamma_ns, a1, a0, nf) + return lo_exact(gamma_ns, a1, a0, betalist) if method == "ordered-truncated": - return eko_ordered_truncated(gamma_ns, a1, a0, nf, order, ev_op_iterations) + return eko_ordered_truncated( + gamma_ns, a1, a0, betalist, order, ev_op_iterations + ) if method == "truncated": - return eko_truncated(gamma_ns, a1, a0, nf, order, ev_op_iterations) + return eko_truncated(gamma_ns, a1, a0, betalist, order, ev_op_iterations) # NLO if order[0] == 2: @@ -388,9 +401,9 @@ def dispatcher( "decompose-expanded", "perturbative-expanded", ]: - return nlo_expanded(gamma_ns, a1, a0, nf) + return nlo_expanded(gamma_ns, a1, a0, betalist) # if method in ["iterate-exact", "decompose-exact", "perturbative-exact"]: - return nlo_exact(gamma_ns, a1, a0, nf) + return nlo_exact(gamma_ns, a1, a0, betalist) # NNLO if order[0] == 3: if method in [ @@ -398,8 +411,8 @@ def dispatcher( "decompose-expanded", "perturbative-expanded", ]: - return nnlo_expanded(gamma_ns, a1, a0, nf) - return nnlo_exact(gamma_ns, a1, a0, nf) + return nnlo_expanded(gamma_ns, a1, a0, betalist) + return nnlo_exact(gamma_ns, a1, a0, betalist) # N3LO if order[0] == 4: if method in [ diff --git a/src/eko/kernels/non_singlet_qed.py b/src/eko/kernels/non_singlet_qed.py index 9e13a8fe5..9af0664ff 100644 --- a/src/eko/kernels/non_singlet_qed.py +++ b/src/eko/kernels/non_singlet_qed.py @@ -2,70 +2,56 @@ import numba as nb import numpy as np -from . import evolution_integrals_qed as ei +from .. import beta +from . import non_singlet as ns from . import utils @nb.njit(cache=True) -def as1aem1_exact(gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from): - """O(as1aem1) non-singlet exact EKO. +def contract_gammas(gamma_ns, aem): + """Contract anomalous dimension along the QED axis. Parameters ---------- - gamma_ns : numpy.ndarray + gamma_ns : 2D numpy.ndarray non-singlet anomalous dimensions - a1 : float - target coupling value - a0 : float - initial coupling value aem : float electromagnetic coupling value - nf : int - number of active flavors Returns ------- - e_ns^0 : complex - O(as1aem1) non-singlet exact EKO + gamma_ns : 1D numpy.ndarray + non-singlet anomalous dimensions """ - return np.exp( - (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j12(a1, a0, aem, nf) - + aem * gamma_ns[0, 1] * np.log(mu2_from / mu2_to) - ) + alphas = np.array([aem**i for i in range(gamma_ns.shape[1])]) + return gamma_ns @ alphas @nb.njit(cache=True) -def as1aem2_exact(gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from): - """ - O(as1aem2) non-singlet exact EKO. +def apply_qed(gamma_pure_qed, mu2_from, mu2_to): + """Apply pure QED evolution to QCD kernel. Parameters ---------- - gamma_ns : numpy.ndarray - non-singlet anomalous dimensions - a1 : float - target coupling value - a0 : float - initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors + gamma_ns : float + pure QED part of the AD + mu2_from : float + initial value of mu2 + mu2_from : float + final value of mu2 Returns ------- - e_ns^0 : complex - O(as1aem2) non-singlet exact EKO + exp : float + pure QED evolution kernel + """ - return np.exp( - (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j12(a1, a0, aem, nf) - + (aem * gamma_ns[0, 1] + aem**2 * gamma_ns[0, 2]) * np.log(mu2_from / mu2_to) - ) + return np.exp(gamma_pure_qed * np.log(mu2_from / mu2_to)) @nb.njit(cache=True) -def as2aem1_exact(gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from): - """O(as2aem1) non-singlet exact EKO. +def as1_exact(gamma_ns, a1, a0, beta): + """O(as1aem1) non-singlet exact EKO. Parameters ---------- @@ -82,19 +68,15 @@ def as2aem1_exact(gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from): Returns ------- - e_ns^1 : complex - O(as2aem1) non-singlet exact EKO + e_ns^0 : complex + O(as1aem1) non-singlet exact EKO """ - return np.exp( - (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j13_exact(a1, a0, aem, nf) - + gamma_ns[2, 0] * ei.j23_exact(a1, a0, aem, nf) - + aem * gamma_ns[0, 1] * np.log(mu2_from / mu2_to) - ) + return ns.lo_exact(gamma_ns, a1, a0, beta) @nb.njit(cache=True) -def as2aem2_exact(gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from): - """O(as2aem2) non-singlet exact EKO. +def as2_exact(gamma_ns, a1, a0, beta): + """O(as2aem1) non-singlet exact EKO. Parameters ---------- @@ -112,17 +94,13 @@ def as2aem2_exact(gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from): Returns ------- e_ns^1 : complex - O(as2aem2) non-singlet exact EKO + O(as2aem1) non-singlet exact EKO """ - return np.exp( - (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j13_exact(a1, a0, aem, nf) - + gamma_ns[2, 0] * ei.j23_exact(a1, a0, aem, nf) - + (aem * gamma_ns[0, 1] + aem**2 * gamma_ns[0, 2]) * np.log(mu2_from / mu2_to) - ) + return ns.nlo_exact(gamma_ns, a1, a0, beta) @nb.njit(cache=True) -def as3aem1_exact(gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from): +def as3_exact(gamma_ns, a1, a0, beta): """O(as3aem1) non-singlet exact EKO. Parameters @@ -143,43 +121,7 @@ def as3aem1_exact(gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from): e_ns^2 : complex O(as3aem1) non-singlet exact EKO """ - return np.exp( - (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j14_exact(a1, a0, aem, nf) - + gamma_ns[2, 0] * ei.j24_exact(a1, a0, aem, nf) - + gamma_ns[3, 0] * ei.j34_exact(a1, a0, aem, nf) - + aem * gamma_ns[0, 1] * np.log(mu2_from / mu2_to) - ) - - -@nb.njit(cache=True) -def as3aem2_exact(gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from): - """ - O(as3aem2) non-singlet exact EKO. - - Parameters - ---------- - gamma_ns : numpy.ndarray - non-singlet anomalous dimensions - a1 : float - target coupling value - a0 : float - initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors - - Returns - ------- - e_ns^2 : complex - O(as3aem2) non-singlet exact EKO - """ - return np.exp( - (gamma_ns[1, 0] + aem * gamma_ns[1, 1]) * ei.j14_exact(a1, a0, aem, nf) - + gamma_ns[2, 0] * ei.j24_exact(a1, a0, aem, nf) - + gamma_ns[3, 0] * ei.j34_exact(a1, a0, aem, nf) - + (aem * gamma_ns[0, 1] + aem**2 * gamma_ns[0, 2]) * np.log(mu2_from / mu2_to) - ) + return ns.nnlo_exact(gamma_ns, a1, a0, beta) @nb.njit(cache=True) @@ -259,21 +201,18 @@ def fixed_alphaem_exact(order, gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from): e_ns : complex non-singlet EKO """ - if order[1] == 1: - if order[0] == 1: - return as1aem1_exact(gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from) - if order[0] == 2: - return as2aem1_exact(gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from) - if order[0] == 3: - return as3aem1_exact(gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from) - if order[1] == 2: - if order[0] == 1: - return as1aem2_exact(gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from) - if order[0] == 2: - return as2aem2_exact(gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from) - if order[0] == 3: - return as3aem2_exact(gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from) - raise NotImplementedError("Selected order is not implemented") + betalist = [beta.beta_qcd((2 + i, 0), nf) for i in range(order[0])] + betalist[0] += aem * beta.beta_qcd((2, 1), nf) + gamma_ns_list = contract_gammas(gamma_ns, aem) + if order[0] == 1: + qcd_only = as1_exact(gamma_ns_list[1:], a1, a0, betalist) + elif order[0] == 2: + qcd_only = as2_exact(gamma_ns_list[1:], a1, a0, betalist) + elif order[0] == 3: + qcd_only = as3_exact(gamma_ns_list[1:], a1, a0, betalist) + else: + raise NotImplementedError("Selected order is not implemented") + return qcd_only * apply_qed(gamma_ns_list[0], mu2_from, mu2_to) @nb.njit(cache=True) @@ -306,80 +245,22 @@ def running_alphaem_exact( """ a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) res = 1.0 + betalist = [beta.beta_qcd((2 + i, 0), nf) for i in range(order[0])] # For the moment implemented in this way to make numba compile it # TODO : implement it with np.prod in a way that numba compiles it - if order[1] == 1: - if order[0] == 1: - for step in range(1, ev_op_iterations + 1): - res *= as1aem1_exact( - gamma_ns, - a_steps[step], - a_steps[step - 1], - aem_list[step - 1], - nf, - mu2_to, - mu2_from, - ) - return res - if order[0] == 2: - for step in range(1, ev_op_iterations + 1): - res *= as2aem1_exact( - gamma_ns, - a_steps[step], - a_steps[step - 1], - aem_list[step - 1], - nf, - mu2_to, - mu2_from, - ) - return res - if order[0] == 3: - for step in range(1, ev_op_iterations + 1): - res *= as3aem1_exact( - gamma_ns, - a_steps[step], - a_steps[step - 1], - aem_list[step - 1], - nf, - mu2_to, - mu2_from, - ) - return res - if order[1] == 2: + for step in range(1, ev_op_iterations + 1): + aem = aem_list[step - 1] + a1 = a_steps[step] + a0 = a_steps[step - 1] + betalist[0] += aem * beta.beta_qcd((2, 1), nf) + gamma_ns_list = contract_gammas(gamma_ns, aem) if order[0] == 1: - for step in range(1, ev_op_iterations + 1): - res *= as1aem2_exact( - gamma_ns, - a_steps[step], - a_steps[step - 1], - aem_list[step - 1], - nf, - mu2_to, - mu2_from, - ) - return res - if order[0] == 2: - for step in range(1, ev_op_iterations + 1): - res *= as2aem2_exact( - gamma_ns, - a_steps[step], - a_steps[step - 1], - aem_list[step - 1], - nf, - mu2_to, - mu2_from, - ) - return res - if order[0] == 3: - for step in range(1, ev_op_iterations + 1): - res *= as3aem2_exact( - gamma_ns, - a_steps[step], - a_steps[step - 1], - aem_list[step - 1], - nf, - mu2_to, - mu2_from, - ) - return res - raise NotImplementedError("Selected order is not implemented") + res *= as1_exact(gamma_ns_list[1:], a1, a0, betalist) + elif order[0] == 2: + res *= as2_exact(gamma_ns_list[1:], a1, a0, betalist) + elif order[0] == 3: + res *= as3_exact(gamma_ns_list[1:], a1, a0, betalist) + else: + raise NotImplementedError("Selected order is not implemented") + res *= apply_qed(gamma_ns_list[0], mu2_from, mu2_to) + return res diff --git a/src/eko/kernels/singlet.py b/src/eko/kernels/singlet.py index 8d93a2eeb..c2bea2a51 100644 --- a/src/eko/kernels/singlet.py +++ b/src/eko/kernels/singlet.py @@ -6,12 +6,12 @@ from .. import anomalous_dimensions as ad from .. import beta from . import as4_evolution_integrals as as4_ei -from . import evolution_integrals_qcd as ei +from . import evolution_integrals as ei from . import utils @nb.njit(cache=True) -def lo_exact(gamma_singlet, a1, a0, nf): +def lo_exact(gamma_singlet, a1, a0, beta): """Singlet leading order exact EKO. Parameters @@ -30,7 +30,7 @@ def lo_exact(gamma_singlet, a1, a0, nf): numpy.ndarray singlet leading order exact EKO """ - return ad.exp_matrix_2D(gamma_singlet[0] * ei.j12(a1, a0, nf))[0] + return ad.exp_matrix_2D(gamma_singlet[0] * ei.j12(a1, a0, beta[0]))[0] @nb.njit(cache=True) @@ -61,7 +61,7 @@ def nlo_decompose(gamma_singlet, j01, j11): @nb.njit(cache=True) -def nlo_decompose_exact(gamma_singlet, a1, a0, nf): +def nlo_decompose_exact(gamma_singlet, a1, a0, beta): """Singlet next-to-leading order decompose-exact EKO. Parameters @@ -80,13 +80,17 @@ def nlo_decompose_exact(gamma_singlet, a1, a0, nf): numpy.ndarray singlet next-to-leading order decompose-exact EKO """ + beta0 = beta[0] + b_vec = [betas / beta0 for betas in beta] return nlo_decompose( - gamma_singlet, ei.j13_exact(a1, a0, nf), ei.j23_exact(a1, a0, nf) + gamma_singlet, + ei.j13_exact(a1, a0, beta0, b_vec), + ei.j23_exact(a1, a0, beta0, b_vec), ) @nb.njit(cache=True) -def nlo_decompose_expanded(gamma_singlet, a1, a0, nf): +def nlo_decompose_expanded(gamma_singlet, a1, a0, beta): """Singlet next-to-leading order decompose-expanded EKO. Parameters @@ -105,8 +109,12 @@ def nlo_decompose_expanded(gamma_singlet, a1, a0, nf): numpy.ndarray singlet next-to-leading order decompose-expanded EKO """ + beta0 = beta[0] + b_vec = [betas / beta0 for betas in beta] return nlo_decompose( - gamma_singlet, ei.j13_expanded(a1, a0, nf), ei.j23_expanded(a1, a0, nf) + gamma_singlet, + ei.j13_expanded(a1, a0, beta0, b_vec), + ei.j23_expanded(a1, a0, beta0), ) @@ -142,7 +150,7 @@ def nnlo_decompose(gamma_singlet, j02, j12, j22): @nb.njit(cache=True) -def nnlo_decompose_exact(gamma_singlet, a1, a0, nf): +def nnlo_decompose_exact(gamma_singlet, a1, a0, beta): """Singlet next-to-next-to-leading order decompose-exact EKO. Parameters @@ -161,16 +169,18 @@ def nnlo_decompose_exact(gamma_singlet, a1, a0, nf): numpy.ndarray singlet next-to-next-to-leading order decompose-exact EKO """ + beta0 = beta[0] + b_vec = [betas / beta0 for betas in beta] return nnlo_decompose( gamma_singlet, - ei.j14_exact(a1, a0, nf), - ei.j24_exact(a1, a0, nf), - ei.j34_exact(a1, a0, nf), + ei.j14_exact(a1, a0, beta0, b_vec), + ei.j24_exact(a1, a0, beta0, b_vec), + ei.j34_exact(a1, a0, beta0, b_vec), ) @nb.njit(cache=True) -def nnlo_decompose_expanded(gamma_singlet, a1, a0, nf): +def nnlo_decompose_expanded(gamma_singlet, a1, a0, beta): """Singlet next-to-next-to-leading order decompose-expanded EKO. Parameters @@ -189,11 +199,13 @@ def nnlo_decompose_expanded(gamma_singlet, a1, a0, nf): numpy.ndarray singlet next-to-next-to-leading order decompose-expanded EKO """ + beta0 = beta[0] + b_vec = [betas / beta0 for betas in beta] return nnlo_decompose( gamma_singlet, - ei.j14_expanded(a1, a0, nf), - ei.j24_expanded(a1, a0, nf), - ei.j34_expanded(a1, a0, nf), + ei.j14_expanded(a1, a0, beta0, b_vec), + ei.j24_expanded(a1, a0, beta0, b_vec), + ei.j34_expanded(a1, a0, beta0), ) @@ -260,7 +272,7 @@ def n3lo_decompose_exact(gamma_singlet, a1, a0, nf): beta.b_qcd((5, 0), nf), ] roots = as4_ei.roots(b_list) - j12 = ei.j12(a1, a0, nf) + j12 = ei.j12(a1, a0, beta0) j13 = as4_ei.j13_exact(a1, a0, beta0, b_list, roots) j23 = as4_ei.j23_exact(a1, a0, beta0, b_list, roots) j33 = as4_ei.j33_exact(a1, a0, beta0, b_list, roots) @@ -305,7 +317,7 @@ def n3lo_decompose_expanded(gamma_singlet, a1, a0, nf): @nb.njit(cache=True) -def eko_iterate(gamma_singlet, a1, a0, nf, order, ev_op_iterations): +def eko_iterate(gamma_singlet, a1, a0, beta_vec, order, ev_op_iterations): """Singlet |NLO|, |NNLO| or |N3LO| iterated (exact) EKO. Parameters @@ -329,11 +341,6 @@ def eko_iterate(gamma_singlet, a1, a0, nf, order, ev_op_iterations): singlet iterated (exact) EKO """ a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) - beta_vec = [beta.beta_qcd((2, 0), nf), beta.beta_qcd((3, 0), nf)] - if order[0] >= 3: - beta_vec.append(beta.beta_qcd((4, 0), nf)) - if order[0] >= 4: - beta_vec.append(beta.beta_qcd((5, 0), nf)) e = np.identity(2, np.complex_) al = a_steps[0] for ah in a_steps[1:]: @@ -352,7 +359,7 @@ def eko_iterate(gamma_singlet, a1, a0, nf, order, ev_op_iterations): @nb.njit(cache=True) -def r_vec(gamma_singlet, nf, ev_op_max_order, order, is_exact): +def r_vec(gamma_singlet, beta, ev_op_max_order, order, is_exact): r"""Compute singlet R vector for perturbative mode. .. math:: @@ -380,17 +387,17 @@ def r_vec(gamma_singlet, nf, ev_op_max_order, order, is_exact): r = np.zeros( (ev_op_max_order[0] + 1, 2, 2), dtype=np.complex_ ) # k = 0 .. max_order - beta0 = beta.beta_qcd((2, 0), nf) + beta0 = beta[0] # fill explicit elements r[0] = gamma_singlet[0] / beta0 if order[0] > 1: - b1 = beta.b_qcd((3, 0), nf) + b1 = beta[1] / beta0 r[1] = gamma_singlet[1] / beta0 - b1 * r[0] if order[0] > 2: - b2 = beta.b_qcd((4, 0), nf) + b2 = beta[2] / beta0 r[2] = gamma_singlet[2] / beta0 - b1 * r[1] - b2 * r[0] if order[0] > 3: - b3 = beta.b_qcd((5, 0), nf) + b3 = beta[3] / beta0 r[3] = gamma_singlet[3] / beta0 - b1 * r[2] - b2 * r[1] - b3 * r[0] # fill rest if is_exact: @@ -478,7 +485,7 @@ def sum_u(uvec, a): @nb.njit(cache=True) def eko_perturbative( - gamma_singlet, a1, a0, nf, order, ev_op_iterations, ev_op_max_order, is_exact + gamma_singlet, a1, a0, beta, order, ev_op_iterations, ev_op_max_order, is_exact ): """Singlet |NLO|,|NNLO| or |N3LO| perturbative EKO, depending on which r is passed. @@ -506,14 +513,14 @@ def eko_perturbative( numpy.ndarray singlet perturbative EKO """ - r = r_vec(gamma_singlet, nf, ev_op_max_order, order, is_exact) + r = r_vec(gamma_singlet, beta, ev_op_max_order, order, is_exact) uk = u_vec(r, ev_op_max_order) e = np.identity(2, np.complex_) # iterate elements a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) al = a_steps[0] for ah in a_steps[1:]: - e0 = lo_exact(gamma_singlet, ah, al, nf) + e0 = lo_exact(gamma_singlet, ah, al, beta) uh = sum_u(uk, ah) ul = sum_u(uk, al) # join elements @@ -524,7 +531,7 @@ def eko_perturbative( @nb.njit(cache=True) -def eko_truncated(gamma_singlet, a1, a0, nf, order, ev_op_iterations): +def eko_truncated(gamma_singlet, a1, a0, beta, order, ev_op_iterations): """Singlet |NLO|, |NNLO| or |N3LO| truncated EKO. Parameters @@ -547,7 +554,7 @@ def eko_truncated(gamma_singlet, a1, a0, nf, order, ev_op_iterations): numpy.ndarray singlet truncated EKO """ - r = r_vec(gamma_singlet, nf, order, order, False) + r = r_vec(gamma_singlet, beta, order, order, False) u = u_vec(r, order) u1 = np.ascontiguousarray(u[1]) e = np.identity(2, np.complex_) @@ -555,7 +562,7 @@ def eko_truncated(gamma_singlet, a1, a0, nf, order, ev_op_iterations): a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) al = a_steps[0] for ah in a_steps[1:]: - e0 = np.ascontiguousarray(lo_exact(gamma_singlet, ah, al, nf)) + e0 = np.ascontiguousarray(lo_exact(gamma_singlet, ah, al, beta)) if order[0] >= 2: ek = e0 + ah * u1 @ e0 - al * e0 @ u1 if order[0] >= 3: @@ -610,38 +617,53 @@ def dispatcher( # pylint: disable=too-many-return-statements numpy.ndarray singlet EKO """ + betalist = [beta.beta_qcd((2 + i, 0), nf) for i in range(order[0])] # for SV expanded we still fall in here, but we don't need to do anything if a1 == a0: return np.eye(len(gamma_singlet[0]), dtype=np.complex_) # use always exact in LO if order[0] == 1: - return lo_exact(gamma_singlet, a1, a0, nf) + return lo_exact(gamma_singlet, a1, a0, betalist) # Common method for NLO and NNLO if method in ["iterate-exact", "iterate-expanded"]: - return eko_iterate(gamma_singlet, a1, a0, nf, order, ev_op_iterations) + return eko_iterate(gamma_singlet, a1, a0, betalist, order, ev_op_iterations) if method == "perturbative-exact": return eko_perturbative( - gamma_singlet, a1, a0, nf, order, ev_op_iterations, ev_op_max_order, True + gamma_singlet, + a1, + a0, + betalist, + order, + ev_op_iterations, + ev_op_max_order, + True, ) if method == "perturbative-expanded": return eko_perturbative( - gamma_singlet, a1, a0, nf, order, ev_op_iterations, ev_op_max_order, False + gamma_singlet, + a1, + a0, + betalist, + order, + ev_op_iterations, + ev_op_max_order, + False, ) if method in ["truncated", "ordered-truncated"]: - return eko_truncated(gamma_singlet, a1, a0, nf, order, ev_op_iterations) + return eko_truncated(gamma_singlet, a1, a0, betalist, order, ev_op_iterations) # These methods are scattered for nlo and nnlo if method == "decompose-exact": if order[0] == 2: - return nlo_decompose_exact(gamma_singlet, a1, a0, nf) + return nlo_decompose_exact(gamma_singlet, a1, a0, betalist) elif order[0] == 3: - return nnlo_decompose_exact(gamma_singlet, a1, a0, nf) + return nnlo_decompose_exact(gamma_singlet, a1, a0, betalist) return n3lo_decompose_exact(gamma_singlet, a1, a0, nf) if method == "decompose-expanded": if order[0] == 2: - return nlo_decompose_expanded(gamma_singlet, a1, a0, nf) + return nlo_decompose_expanded(gamma_singlet, a1, a0, betalist) elif order[0] == 3: - return nnlo_decompose_expanded(gamma_singlet, a1, a0, nf) + return nnlo_decompose_expanded(gamma_singlet, a1, a0, betalist) return n3lo_decompose_expanded(gamma_singlet, a1, a0, nf) raise NotImplementedError("Selected method is not implemented") diff --git a/tests/eko/kernels/test_as4_ei.py b/tests/eko/kernels/test_as4_ei.py index 98ba546d9..acfec184e 100644 --- a/tests/eko/kernels/test_as4_ei.py +++ b/tests/eko/kernels/test_as4_ei.py @@ -2,7 +2,7 @@ from eko import beta from eko.kernels import as4_evolution_integrals as as4_ei -from eko.kernels.evolution_integrals_qcd import j12 +from eko.kernels.evolution_integrals import j12 def test_zero(): @@ -81,8 +81,8 @@ def test_der_n3lo_exa(): # 03 rhs = 1.0 / (a1 * den) - j00p = j12(a1 + 0.5 * delta_a, a0, nf) - j00m = j12(a1 - 0.5 * delta_a, a0, nf) + j00p = j12(a1 + 0.5 * delta_a, a0, beta0) + j00m = j12(a1 - 0.5 * delta_a, a0, beta0) lhs = ( as4_ei.j03_exact(j00p, j13p, j23p, j33p, b_list) - as4_ei.j03_exact(j00m, j13m, j23m, j33m, b_list) @@ -129,8 +129,8 @@ def test_der_n3lo_exp(): # 03 rhs = 1.0 / (a1 * den) - j00p = j12(a1 + 0.5 * delta_a, a0, nf) - j00m = j12(a1 - 0.5 * delta_a, a0, nf) + j00p = j12(a1 + 0.5 * delta_a, a0, beta0) + j00m = j12(a1 - 0.5 * delta_a, a0, beta0) lhs = ( as4_ei.j03_expanded(j00p, j13p, j23p, j33p, b_list) - as4_ei.j03_expanded(j00m, j13m, j23m, j33m, b_list) diff --git a/tests/eko/kernels/test_ei.py b/tests/eko/kernels/test_ei.py index 54d9d8544..ed7fc27c9 100644 --- a/tests/eko/kernels/test_ei.py +++ b/tests/eko/kernels/test_ei.py @@ -1,55 +1,68 @@ import numpy as np from eko import beta -from eko.kernels import evolution_integrals_qcd as ei -from eko.kernels import evolution_integrals_qed as ei_qed +from eko.kernels import evolution_integrals as ei def test_zero(): """No evolution results in exp(0)""" nf = 3 + beta0 = beta.beta_qcd((2, 0), nf) + b_vec = [beta.beta_qcd((2 + i, 0), nf) / beta0 for i in range(0, 2 + 1)] for fnc in [ - ei.j12, ei.j13_exact, ei.j13_expanded, ei.j23_exact, - ei.j23_expanded, ei.j14_exact, ei.j14_expanded, ei.j24_exact, ei.j24_expanded, ei.j34_exact, + ]: + np.testing.assert_allclose(fnc(1, 1, beta0, b_vec), 0) + for fnc in [ + ei.j12, + ei.j23_expanded, ei.j34_expanded, ]: - np.testing.assert_allclose(fnc(1, 1, nf), 0) + np.testing.assert_allclose(fnc(1, 1, beta0), 0) def test_zero_qed(): """No evolution results in exp(0)""" + aem = 0.00058 nf = 3 + beta0 = beta.beta_qcd((2, 0), nf) + aem * beta.beta_qcd((2, 1), nf) + b_vec = [beta.beta_qcd((2 + i, 0), nf) / beta0 for i in range(0, 2 + 1)] + for fnc in [ + ei.j23_exact, + ei.j13_exact, + ei.j03_exact, + ei.j34_exact, + ei.j24_exact, + ei.j14_exact, + ei.j04_exact, + ]: + np.testing.assert_allclose(fnc(1, 1, beta0, b_vec), 0) for fnc in [ - ei_qed.j12, - ei_qed.j02, - ei_qed.j23_exact, - ei_qed.j13_exact, - ei_qed.j03_exact, - ei_qed.j34_exact, - ei_qed.j24_exact, - ei_qed.j14_exact, - ei_qed.j04_exact, + ei.j12, + ei.j02, + ei.j23_expanded, + ei.j34_expanded, ]: - np.testing.assert_allclose(fnc(1, 1, 0.00058, nf), 0) + np.testing.assert_allclose(fnc(1, 1, beta0), 0) def test_der_lo(): """LO derivative""" nf = 3 + beta0 = beta.beta_qcd((2, 0), nf) a0 = 5 a1 = 3 delta_a = -1e-6 rhs = 1.0 / (beta.beta_qcd((2, 0), nf) * a1) lhs = ( - ei.j12(a1 + 0.5 * delta_a, a0, nf) - ei.j12(a1 - 0.5 * delta_a, a0, nf) + ei.j12(a1 + 0.5 * delta_a, a0, beta0) - ei.j12(a1 - 0.5 * delta_a, a0, beta0) ) / delta_a np.testing.assert_allclose(rhs, lhs) @@ -57,14 +70,16 @@ def test_der_lo(): def test_der_nlo_exp(): """expanded NLO derivative""" nf = 3 + beta0 = beta.beta_qcd((2, 0), nf) + b_vec = [beta.beta_qcd((2 + i, 0), nf) / beta0 for i in range(0, 2 + 1)] a0 = 0.3 a1 = 0.1 delta_a = -1e-6 # 01 rhs = 1.0 / (beta.beta_qcd((2, 0), nf) * a1 + beta.beta_qcd((3, 0), nf) * a1**2) lhs = ( - ei.j13_expanded(a1 + 0.5 * delta_a, a0, nf) - - ei.j13_expanded(a1 - 0.5 * delta_a, a0, nf) + ei.j13_expanded(a1 + 0.5 * delta_a, a0, beta0, b_vec) + - ei.j13_expanded(a1 - 0.5 * delta_a, a0, beta0, b_vec) ) / delta_a np.testing.assert_allclose( rhs, lhs, atol=np.abs((beta.b_qcd((3, 0), nf) * a1) ** 2) @@ -72,8 +87,8 @@ def test_der_nlo_exp(): # 11 rhs = 1.0 / (beta.beta_qcd((2, 0), nf) + beta.beta_qcd((3, 0), nf) * a1) lhs = ( - ei.j23_expanded(a1 + 0.5 * delta_a, a0, nf) - - ei.j23_expanded(a1 - 0.5 * delta_a, a0, nf) + ei.j23_expanded(a1 + 0.5 * delta_a, a0, beta0) + - ei.j23_expanded(a1 - 0.5 * delta_a, a0, beta0) ) / delta_a np.testing.assert_allclose(rhs, lhs, atol=np.abs(beta.b_qcd((3, 0), nf) * a1)) @@ -81,21 +96,23 @@ def test_der_nlo_exp(): def test_der_nlo_exa(): """exact NLO derivative""" nf = 3 + beta0 = beta.beta_qcd((2, 0), nf) + b_vec = [beta.beta_qcd((2 + i, 0), nf) / beta0 for i in range(0, 2 + 1)] a0 = 0.3 a1 = 0.1 delta_a = -1e-6 # 01 rhs = 1.0 / (beta.beta_qcd((2, 0), nf) * a1 + beta.beta_qcd((3, 0), nf) * a1**2) lhs = ( - ei.j13_exact(a1 + 0.5 * delta_a, a0, nf) - - ei.j13_exact(a1 - 0.5 * delta_a, a0, nf) + ei.j13_exact(a1 + 0.5 * delta_a, a0, beta0, b_vec) + - ei.j13_exact(a1 - 0.5 * delta_a, a0, beta0, b_vec) ) / delta_a np.testing.assert_allclose(rhs, lhs, atol=np.abs(delta_a)) # in fact O(delta_a^2) # 11 rhs = 1.0 / (beta.beta_qcd((2, 0), nf) + beta.beta_qcd((3, 0), nf) * a1) lhs = ( - ei.j23_exact(a1 + 0.5 * delta_a, a0, nf) - - ei.j23_exact(a1 - 0.5 * delta_a, a0, nf) + ei.j23_exact(a1 + 0.5 * delta_a, a0, beta0, b_vec) + - ei.j23_exact(a1 - 0.5 * delta_a, a0, beta0, b_vec) ) / delta_a np.testing.assert_allclose(rhs, lhs, atol=np.abs(delta_a)) # in fact O(delta_a^2) @@ -103,6 +120,8 @@ def test_der_nlo_exa(): def test_der_nnlo_exp(): """expanded NNLO derivative""" nf = 3 + beta0 = beta.beta_qcd((2, 0), nf) + b_vec = [beta.beta_qcd((2 + i, 0), nf) / beta0 for i in range(0, 2 + 1)] a0 = 0.3 a1 = 0.1 delta_a = -1e-6 @@ -117,8 +136,8 @@ def test_der_nnlo_exp(): + beta.beta_qcd((4, 0), nf) * a1**3 ) lhs = ( - ei.j14_expanded(a1 + 0.5 * delta_a, a0, nf) - - ei.j14_expanded(a1 - 0.5 * delta_a, a0, nf) + ei.j14_expanded(a1 + 0.5 * delta_a, a0, beta0, b_vec) + - ei.j14_expanded(a1 - 0.5 * delta_a, a0, beta0, b_vec) ) / delta_a toll = ( ( @@ -136,8 +155,8 @@ def test_der_nnlo_exp(): + beta.beta_qcd((4, 0), nf) * a1**2 ) lhs = ( - ei.j24_expanded(a1 + 0.5 * delta_a, a0, nf) - - ei.j24_expanded(a1 - 0.5 * delta_a, a0, nf) + ei.j24_expanded(a1 + 0.5 * delta_a, a0, beta0, b_vec) + - ei.j24_expanded(a1 - 0.5 * delta_a, a0, beta0, b_vec) ) / delta_a toll = ( (beta.b_qcd((3, 0), nf) ** 2 - beta.b_qcd((4, 0), nf)) @@ -152,8 +171,8 @@ def test_der_nnlo_exp(): + beta.beta_qcd((4, 0), nf) * a1**2 ) lhs = ( - ei.j34_expanded(a1 + 0.5 * delta_a, a0, nf) - - ei.j34_expanded(a1 - 0.5 * delta_a, a0, nf) + ei.j34_expanded(a1 + 0.5 * delta_a, a0, beta0) + - ei.j34_expanded(a1 - 0.5 * delta_a, a0, beta0) ) / delta_a np.testing.assert_allclose( rhs, @@ -165,6 +184,8 @@ def test_der_nnlo_exp(): def test_der_nnlo_exa(): """exact NNLO derivative""" nf = 3 + beta0 = beta.beta_qcd((2, 0), nf) + b_vec = [beta.beta_qcd((2 + i, 0), nf) / beta0 for i in range(0, 2 + 1)] a0 = 0.3 a1 = 0.1 delta_a = -1e-6 @@ -175,8 +196,8 @@ def test_der_nnlo_exa(): + beta.beta_qcd((4, 0), nf) * a1**3 ) lhs = ( - ei.j14_exact(a1 + 0.5 * delta_a, a0, nf) - - ei.j14_exact(a1 - 0.5 * delta_a, a0, nf) + ei.j14_exact(a1 + 0.5 * delta_a, a0, beta0, b_vec) + - ei.j14_exact(a1 - 0.5 * delta_a, a0, beta0, b_vec) ) / delta_a np.testing.assert_allclose(rhs, lhs, atol=np.abs(delta_a)) # in fact O(delta_a^2) # 12 @@ -186,8 +207,8 @@ def test_der_nnlo_exa(): + beta.beta_qcd((4, 0), nf) * a1**2 ) lhs = ( - ei.j24_exact(a1 + 0.5 * delta_a, a0, nf) - - ei.j24_exact(a1 - 0.5 * delta_a, a0, nf) + ei.j24_exact(a1 + 0.5 * delta_a, a0, beta0, b_vec) + - ei.j24_exact(a1 - 0.5 * delta_a, a0, beta0, b_vec) ) / delta_a np.testing.assert_allclose(rhs, lhs, atol=np.abs(delta_a)) # in fact O(delta_a^2) # 12 @@ -197,7 +218,7 @@ def test_der_nnlo_exa(): + beta.beta_qcd((4, 0), nf) * a1**2 ) lhs = ( - ei.j34_exact(a1 + 0.5 * delta_a, a0, nf) - - ei.j34_exact(a1 - 0.5 * delta_a, a0, nf) + ei.j34_exact(a1 + 0.5 * delta_a, a0, beta0, b_vec) + - ei.j34_exact(a1 - 0.5 * delta_a, a0, beta0, b_vec) ) / delta_a np.testing.assert_allclose(rhs, lhs, atol=np.abs(delta_a)) # in fact O(delta_a^2) diff --git a/tests/eko/kernels/test_ns.py b/tests/eko/kernels/test_ns.py index 17d4964b8..68c1ebe5b 100644 --- a/tests/eko/kernels/test_ns.py +++ b/tests/eko/kernels/test_ns.py @@ -203,7 +203,7 @@ def test_ode_n3lo(): def test_error(): - with pytest.raises(NotImplementedError): + with pytest.raises(ValueError): ns.dispatcher((5, 0), "iterate-exact", np.random.rand(3) + 0j, 0.2, 0.1, 3, 10) with pytest.raises(NotImplementedError): ad.gamma_ns((2, 0), 10202, 1, 3) From 6a5adba15e8a15d801b48bce9f721105cb5c3a9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 7 Feb 2023 17:08:34 +0100 Subject: [PATCH 283/312] Make vec_alphaem complex --- src/eko/kernels/non_singlet_qed.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/eko/kernels/non_singlet_qed.py b/src/eko/kernels/non_singlet_qed.py index 9af0664ff..2a5eb93e5 100644 --- a/src/eko/kernels/non_singlet_qed.py +++ b/src/eko/kernels/non_singlet_qed.py @@ -23,8 +23,8 @@ def contract_gammas(gamma_ns, aem): gamma_ns : 1D numpy.ndarray non-singlet anomalous dimensions """ - alphas = np.array([aem**i for i in range(gamma_ns.shape[1])]) - return gamma_ns @ alphas + vec_alphaem = np.array([aem**i for i in range(gamma_ns.shape[1])], np.complex_) + return gamma_ns @ vec_alphaem @nb.njit(cache=True) From 1d1c1f3071b892b378c62d420eec0514c2c7292d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 7 Feb 2023 22:01:43 +0100 Subject: [PATCH 284/312] Add dtype --- src/eko/kernels/non_singlet_qed.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/eko/kernels/non_singlet_qed.py b/src/eko/kernels/non_singlet_qed.py index 2a5eb93e5..69346e79a 100644 --- a/src/eko/kernels/non_singlet_qed.py +++ b/src/eko/kernels/non_singlet_qed.py @@ -23,7 +23,9 @@ def contract_gammas(gamma_ns, aem): gamma_ns : 1D numpy.ndarray non-singlet anomalous dimensions """ - vec_alphaem = np.array([aem**i for i in range(gamma_ns.shape[1])], np.complex_) + vec_alphaem = np.array( + [aem**i for i in range(gamma_ns.shape[1])], dtype=np.complex_ + ) return gamma_ns @ vec_alphaem From 3e09e60b5a467190fe31038e654aaed22ccc3058 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Sat, 18 Feb 2023 16:08:41 +0100 Subject: [PATCH 285/312] Fixing tests --- src/eko/evolution_operator/__init__.py | 71 ++++++++++--------- .../operator_matrix_element.py | 4 +- src/eko/kernels/singlet_qed.py | 3 +- src/eko/kernels/valence_qed.py | 3 +- .../unpolarized/space_like/__init__.py | 2 + tests/eko/evolution_operator/test_init.py | 13 ++-- tests/eko/{ => kernels}/test_kernels_QEDns.py | 2 +- .../{ => kernels}/test_kernels_QEDsinglet.py | 3 +- .../{ => kernels}/test_kernels_QEDvalence.py | 13 ++-- tests/eko/test_couplings.py | 2 +- 10 files changed, 64 insertions(+), 52 deletions(-) rename tests/eko/{ => kernels}/test_kernels_QEDns.py (99%) rename tests/eko/{ => kernels}/test_kernels_QEDsinglet.py (98%) rename tests/eko/{ => kernels}/test_kernels_QEDvalence.py (92%) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index d71120a5d..fcdd7f6ae 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -198,7 +198,6 @@ def quad_ker( as0, mu2_from, mu2_to, - as_raw, aem_list, alphaem_running, nf, @@ -273,13 +272,14 @@ def quad_ker( method, as1, as0, - as_raw, nf, L, ev_op_iterations, ev_op_max_order, sv_mode, is_threshold, + is_polarized, + is_time_like, ) else: ker = quad_ker_qed( @@ -292,7 +292,6 @@ def quad_ker( as0, mu2_from, mu2_to, - as_raw, aem_list, alphaem_running, nf, @@ -316,13 +315,14 @@ def quad_ker_qcd( method, as1, as0, - as_raw, nf, L, ev_op_iterations, ev_op_max_order, sv_mode, is_threshold, + is_polarized, + is_time_like, ): """Raw evolution kernel inside quad. @@ -419,6 +419,7 @@ def quad_ker_qcd( ) if sv_mode == sv.Modes.expanded and not is_threshold: ker = sv.expanded.non_singlet_variation(gamma_ns, as1, order, nf, L) * ker + return ker @nb.njit(cache=True) @@ -432,7 +433,6 @@ def quad_ker_qed( as0, mu2_from, mu2_to, - as_raw, aem_list, alphaem_running, nf, @@ -484,7 +484,7 @@ def quad_ker_qed( """ # compute the actual evolution kernel for QEDxQCD if ker_base.is_QEDsinglet: - gamma_s = ad.gamma_singlet_qed(order, ker_base.n, nf) + gamma_s = ad_us.gamma_singlet_qed(order, ker_base.n, nf) # scale var exponentiated is directly applied on gamma if sv_mode == sv.Modes.exponentiated: gamma_s = sv.exponentiated.gamma_variation_qed( @@ -502,16 +502,15 @@ def quad_ker_qed( ev_op_max_order, ) # scale var expanded is applied on the kernel - # TODO : check as_raw and a_em in expanded scale variations - if sv_mode == sv.Modes.expanded and not is_threshold: - ker = np.ascontiguousarray(ker) @ np.ascontiguousarray( - sv.expanded.singlet_variation_qed( - gamma_s, as_raw, aem_list[-1], alphaem_running, order, nf, L - ) - ) + # if sv_mode == sv.Modes.expanded and not is_threshold: + # ker = np.ascontiguousarray(ker) @ np.ascontiguousarray( + # sv.expanded.singlet_variation_qed( + # gamma_s, as_raw, aem_list[-1], alphaem_running, order, nf, L + # ) + # ) ker = select_QEDsinglet_element(ker, mode0, mode1) elif ker_base.is_QEDvalence: - gamma_v = ad.gamma_valence_qed(order, ker_base.n, nf) + gamma_v = ad_us.gamma_valence_qed(order, ker_base.n, nf) # scale var exponentiated is directly applied on gamma if sv_mode == sv.Modes.exponentiated: gamma_v = sv.exponentiated.gamma_variation_qed( @@ -529,15 +528,15 @@ def quad_ker_qed( ev_op_max_order, ) # scale var expanded is applied on the kernel - if sv_mode == sv.Modes.expanded and not is_threshold: - ker = np.ascontiguousarray( - sv.expanded.valence_variation_qed( - gamma_v, as_raw, aem_list[-1], alphaem_running, order, nf, L - ) - ) @ np.ascontiguousarray(ker) + # if sv_mode == sv.Modes.expanded and not is_threshold: + # ker = np.ascontiguousarray( + # sv.expanded.valence_variation_qed( + # gamma_v, as_raw, aem_list[-1], alphaem_running, order, nf, L + # ) + # ) @ np.ascontiguousarray(ker) ker = select_QEDvalence_element(ker, mode0, mode1) else: - gamma_ns = ad.gamma_ns_qed(order, mode0, ker_base.n, nf) + gamma_ns = ad_us.gamma_ns_qed(order, mode0, ker_base.n, nf) # scale var exponentiated is directly applied on gamma if sv_mode == sv.Modes.exponentiated: gamma_ns = sv.exponentiated.gamma_variation_qed( @@ -556,13 +555,13 @@ def quad_ker_qed( mu2_from, mu2_to, ) - if sv_mode == sv.Modes.expanded and not is_threshold: - ker = ( - sv.expanded.non_singlet_variation_qed( - gamma_ns, as_raw, aem_list[-1], alphaem_running, order, nf, L - ) - * ker - ) + # if sv_mode == sv.Modes.expanded and not is_threshold: + # ker = ( + # sv.expanded.non_singlet_variation_qed( + # gamma_ns, as_raw, aem_list[-1], alphaem_running, order, nf, L + # ) + # * ker + # ) return ker @@ -657,23 +656,26 @@ def compute_a(self): """Return the computed values for :math:`a_s` and :math:`a_{em}`.""" coupling = self.managers["couplings"] a0 = coupling.a( - self.mur2_shift(self.q2_from), fact_scale=self.q2_from, nf_to=self.nf + self.sv_exponentiated_shift(self.q2_from), + fact_scale=self.q2_from, + nf_to=self.nf, ) a1 = coupling.a( - self.mur2_shift(self.q2_to), fact_scale=self.q2_to, nf_to=self.nf + self.sv_exponentiated_shift(self.q2_to), + fact_scale=self.q2_to, + nf_to=self.nf, ) - a_raw = coupling.a(self.q2_to, fact_scale=self.q2_to, nf_to=self.nf) - return (a0, a1, a_raw) + return (a0, a1) @property def a_s(self): """Return the computed values for :math:`a_s`.""" - return (self.a[0][0], self.a[1][0], self.a[2][0]) + return (self.a[0][0], self.a[1][0]) @property def a_em(self): """Return the computed values for :math:`a_{em}`.""" - return (self.a[0][1], self.a[1][1], self.a[2][1]) + return (self.a[0][1], self.a[1][1]) def compute_aem_list_as(self): """Return the list of the couplings for the different values of :math:`a_s`.""" @@ -784,7 +786,6 @@ def quad_ker(self, label, logx, areas): as0=self.a_s[0], mu2_from=self.q2_from, mu2_to=self.q2_to, - as_raw=self.a_s[2], aem_list=self.aem_list_as, alphaem_running=self.alphaem_running, nf=self.nf, diff --git a/src/eko/evolution_operator/operator_matrix_element.py b/src/eko/evolution_operator/operator_matrix_element.py index cb219634c..dd0c72005 100644 --- a/src/eko/evolution_operator/operator_matrix_element.py +++ b/src/eko/evolution_operator/operator_matrix_element.py @@ -325,7 +325,9 @@ def a_s(self): Note that here you need to use :math:`a_s^{n_f+1}` """ sc = self.managers["couplings"] - return sc.a_s(self.mur2_shift(self.q2_from), self.q2_from, nf_to=self.nf + 1) + return sc.a_s( + self.sv_exponentiated_shift(self.q2_from), self.q2_from, nf_to=self.nf + 1 + ) def compute(self): """Compute the actual operators (i.e. run the integrations).""" diff --git a/src/eko/kernels/singlet_qed.py b/src/eko/kernels/singlet_qed.py index 7f882a2a2..4fd48a4b7 100644 --- a/src/eko/kernels/singlet_qed.py +++ b/src/eko/kernels/singlet_qed.py @@ -2,7 +2,8 @@ import numba as nb import numpy as np -from .. import anomalous_dimensions as ad +from ekore import anomalous_dimensions as ad + from .. import beta from . import utils diff --git a/src/eko/kernels/valence_qed.py b/src/eko/kernels/valence_qed.py index 6a9b37f9a..83d5d6844 100644 --- a/src/eko/kernels/valence_qed.py +++ b/src/eko/kernels/valence_qed.py @@ -2,7 +2,8 @@ import numba as nb import numpy as np -from .. import anomalous_dimensions as ad +from ekore import anomalous_dimensions as ad + from .. import beta from . import utils diff --git a/src/ekore/anomalous_dimensions/unpolarized/space_like/__init__.py b/src/ekore/anomalous_dimensions/unpolarized/space_like/__init__.py index 567a0a56e..9ae3398d0 100644 --- a/src/ekore/anomalous_dimensions/unpolarized/space_like/__init__.py +++ b/src/ekore/anomalous_dimensions/unpolarized/space_like/__init__.py @@ -18,6 +18,8 @@ import numba as nb import numpy as np +from eko import constants + from .... import harmonics from . import aem1, aem2, as1, as1aem1, as2, as3, as4 diff --git a/tests/eko/evolution_operator/test_init.py b/tests/eko/evolution_operator/test_init.py index d08655b33..8bdd9ebd5 100644 --- a/tests/eko/evolution_operator/test_init.py +++ b/tests/eko/evolution_operator/test_init.py @@ -31,6 +31,10 @@ def test_quad_ker_errors(): areas=[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]], as1=1, as0=2, + mu2_from=1.0, + mu2_to=2.0, + aem_list=[0.01], + alphaem_running=False, nf=3, L=0, ev_op_iterations=1, @@ -79,7 +83,6 @@ def test_quad_ker(monkeypatch): as0=2, mu2_from=1, mu2_to=2, - as_raw=1, aem_list=[0.00058], alphaem_running=False, nf=3, @@ -88,6 +91,8 @@ def test_quad_ker(monkeypatch): ev_op_max_order=(0, 0), sv_mode=1, is_threshold=False, + is_polarized=False, + is_time_like=False, ) np.testing.assert_allclose(res_ns, res) for label in [(br.non_singlet_pids_map["ns+"], 0), (100, 100)]: @@ -105,7 +110,6 @@ def test_quad_ker(monkeypatch): as0=2, mu2_from=1, mu2_to=2, - as_raw=1, aem_list=[0.00058], alphaem_running=False, nf=3, @@ -114,6 +118,8 @@ def test_quad_ker(monkeypatch): ev_op_max_order=(1, 0), sv_mode=sv, is_threshold=False, + is_polarized=False, + is_time_like=False, ) np.testing.assert_allclose(res_sv, 1.0) for label in [ @@ -139,7 +145,6 @@ def test_quad_ker(monkeypatch): as0=2, mu2_from=1, mu2_to=2, - as_raw=1, aem_list=[0.00058], alphaem_running=False, nf=3, @@ -167,7 +172,6 @@ def test_quad_ker(monkeypatch): as0=2, mu2_from=1, mu2_to=2, - as_raw=1, aem_list=[0.00058], alphaem_running=False, nf=3, @@ -451,7 +455,6 @@ def quad_ker_pegasus( a0, mu2_from, mu2_to, - as_raw, [0.00058], False, nf, diff --git a/tests/eko/test_kernels_QEDns.py b/tests/eko/kernels/test_kernels_QEDns.py similarity index 99% rename from tests/eko/test_kernels_QEDns.py rename to tests/eko/kernels/test_kernels_QEDns.py index 9ddce42d8..f9c9ac24b 100644 --- a/tests/eko/test_kernels_QEDns.py +++ b/tests/eko/kernels/test_kernels_QEDns.py @@ -3,10 +3,10 @@ import numpy as np import pytest -from eko import anomalous_dimensions as ad from eko import basis_rotation as br from eko import beta from eko.kernels import non_singlet_qed as ns +from ekore.anomalous_dimensions.unpolarized import space_like as ad methods = [ # "iterate-expanded", diff --git a/tests/eko/test_kernels_QEDsinglet.py b/tests/eko/kernels/test_kernels_QEDsinglet.py similarity index 98% rename from tests/eko/test_kernels_QEDsinglet.py rename to tests/eko/kernels/test_kernels_QEDsinglet.py index b34e673a0..9fc3a43fb 100644 --- a/tests/eko/test_kernels_QEDsinglet.py +++ b/tests/eko/kernels/test_kernels_QEDsinglet.py @@ -1,11 +1,10 @@ -# -*- coding: utf-8 -*- import warnings import numpy as np import pytest -from eko import anomalous_dimensions as ad from eko.kernels import singlet_qed as s +from ekore.anomalous_dimensions.unpolarized import space_like as ad methods = [ # "iterate-expanded", diff --git a/tests/eko/test_kernels_QEDvalence.py b/tests/eko/kernels/test_kernels_QEDvalence.py similarity index 92% rename from tests/eko/test_kernels_QEDvalence.py rename to tests/eko/kernels/test_kernels_QEDvalence.py index af6e1d436..be0474246 100644 --- a/tests/eko/test_kernels_QEDvalence.py +++ b/tests/eko/kernels/test_kernels_QEDvalence.py @@ -1,12 +1,13 @@ -# -*- coding: utf-8 -*- import warnings import numpy as np import pytest -from eko import anomalous_dimensions as ad from eko.kernels import valence_qed as val +# from ekore.anomalous_dimensions.unpolarized import space_like as ad +from ekore import anomalous_dimensions + methods = [ # "iterate-expanded", # "decompose-expanded", @@ -32,7 +33,7 @@ def test_zero(monkeypatch): + np.random.rand(qcd + 1, qed + 1, 2, 2) * 1j ) monkeypatch.setattr( - ad, + anomalous_dimensions, "exp_matrix_2D", lambda gamma_v: ( gamma_v, @@ -82,9 +83,11 @@ def test_zero_true_gamma(monkeypatch): for qed in range(1, 2 + 1): order = (qcd, qed) n = np.random.rand() - gamma_v = ad.gamma_valence_qed(order, n, nf) + gamma_v = anomalous_dimensions.unpolarized.space_like.gamma_valence_qed( + order, n, nf + ) monkeypatch.setattr( - ad, + anomalous_dimensions, "exp_matrix_2D", lambda gamma_v: ( gamma_v, diff --git a/tests/eko/test_couplings.py b/tests/eko/test_couplings.py index fd63625f4..721859b51 100644 --- a/tests/eko/test_couplings.py +++ b/tests/eko/test_couplings.py @@ -167,7 +167,7 @@ def test_ref(self): ) ) for thresh_setup in thresh_setups: - for order_s in [0, 1, 2, 3, 4]: + for order_s in [1, 2, 3, 4]: for order_em in [0, 1, 2]: for evmod in CouplingEvolutionMethod: # if order_em == 1 and method == "expanded" and order_s != 0: From e2ca2bd8289315a04f766127cacb272855f1e791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Sun, 19 Feb 2023 18:26:31 +0100 Subject: [PATCH 286/312] Fix tests --- .../unpolarized/space_like/test_aem1.py | 14 ++-- .../unpolarized/space_like/test_init.py | 65 +++++++++---------- 2 files changed, 41 insertions(+), 38 deletions(-) diff --git a/tests/ekore/anomalous_dimensions/unpolarized/space_like/test_aem1.py b/tests/ekore/anomalous_dimensions/unpolarized/space_like/test_aem1.py index 974996687..d114ff5df 100644 --- a/tests/ekore/anomalous_dimensions/unpolarized/space_like/test_aem1.py +++ b/tests/ekore/anomalous_dimensions/unpolarized/space_like/test_aem1.py @@ -3,21 +3,22 @@ import ekore.anomalous_dimensions.unpolarized.space_like as ad_us import ekore.harmonics as h +from eko import constants def test_number_conservation(): # number N = complex(1.0, 0.0) - s1 = h.S1(N) - np.testing.assert_almost_equal(ad_us.aem1.gamma_ns(N, s1), 0) + sx = h.sx(N, max_weight=1) + np.testing.assert_almost_equal(ad_us.aem1.gamma_ns(N, sx), 0) def test_quark_momentum_conservation(): # quark momentum N = complex(2.0, 0.0) - s1 = h.S1(N) + sx = h.sx(N, max_weight=1) np.testing.assert_almost_equal( - ad_us.aem1.gamma_ns(N, s1) + ad_us.aem1.gamma_phq(N), + ad_us.aem1.gamma_ns(N, sx) + ad_us.aem1.gamma_phq(N), 0, ) @@ -29,5 +30,8 @@ def test_photon_momentum_conservation(): NU = constants.uplike_flavors(NF) ND = NF - NU np.testing.assert_almost_equal( - ad_us.aem1.gamma_qph(N, NF) + ad_us.aem1.gamma_phph(NF), 0 + constants.eu2 * ad_us.aem1.gamma_qph(N, NU) + + constants.ed2 * ad_us.aem1.gamma_qph(N, ND) + + ad_us.aem1.gamma_phph(NF), + 0, ) diff --git a/tests/ekore/anomalous_dimensions/unpolarized/space_like/test_init.py b/tests/ekore/anomalous_dimensions/unpolarized/space_like/test_init.py index 59bb7e0aa..219a225c6 100644 --- a/tests/ekore/anomalous_dimensions/unpolarized/space_like/test_init.py +++ b/tests/ekore/anomalous_dimensions/unpolarized/space_like/test_init.py @@ -10,7 +10,6 @@ from eko import basis_rotation as br from ekore import anomalous_dimensions as ad from ekore import harmonics -from ekore.anomalous_dimensions.unpolarized.space_like import as1 as ad_as1 NF = 5 @@ -18,7 +17,7 @@ def test_eigensystem_gamma_singlet_0_values(): n = 3 s1 = harmonics.S1(n) - gamma_S_0 = as1.gamma_singlet(3, s1, NF) + gamma_S_0 = ad_us.as1.gamma_singlet(3, s1, NF) res = ad.exp_matrix_2D(gamma_S_0) lambda_p = complex(12.273612971466964, 0) lambda_m = complex(5.015275917421917, 0) @@ -40,15 +39,15 @@ def test_eigensystem_gamma_singlet_0_values(): def test_exp_matrix(): n = 3 s1 = harmonics.S1(n) - gamma_S_0 = as1.gamma_singlet(3, s1, NF) + gamma_S_0 = ad_us.as1.gamma_singlet(3, s1, NF) res = ad.exp_matrix_2D(gamma_S_0)[0] res2 = ad.exp_matrix(gamma_S_0)[0] assert_allclose(res, res2) - gamma_S_0_qed = as1.gamma_singlet_qed(3, s1, NF) + gamma_S_0_qed = ad_us.as1.gamma_singlet_qed(3, s1, NF) res = expm(gamma_S_0_qed) res2 = ad.exp_matrix(gamma_S_0_qed)[0] assert_allclose(res, res2) - gamma_v_0_qed = as1.gamma_valence_qed(3, s1) + gamma_v_0_qed = ad_us.as1.gamma_valence_qed(3, s1) res = expm(gamma_v_0_qed) res2 = ad.exp_matrix(gamma_v_0_qed)[0] assert_allclose(res, res2) @@ -83,17 +82,17 @@ def test_eigensystem_gamma_singlet_projectors_EV(): def test_gamma_ns(): nf = 3 - # as1 + # ad_us.as1 assert_almost_equal( ad_us.gamma_ns((3, 0), br.non_singlet_pids_map["ns+"], 1, nf)[0], 0.0 ) - # as2 + # ad_us.as2 assert_allclose( ad_us.gamma_ns((2, 0), br.non_singlet_pids_map["ns-"], 1, nf), np.zeros(2), atol=2e-6, ) - # as3 + # ad_us.as3 assert_allclose( ad_us.gamma_ns((3, 0), br.non_singlet_pids_map["ns-"], 1, nf), np.zeros(3), @@ -128,66 +127,66 @@ def test_gamma_ns_qed(): nf = 3 # aem1 assert_almost_equal( - ad.gamma_ns_qed((1, 1), br.non_singlet_pids_map["ns-u"], 1, nf), + ad_us.gamma_ns_qed((1, 1), br.non_singlet_pids_map["ns-u"], 1, nf), np.zeros((2, 2)), decimal=5, ) assert_almost_equal( - ad.gamma_ns_qed((1, 1), br.non_singlet_pids_map["ns-d"], 1, nf), + ad_us.gamma_ns_qed((1, 1), br.non_singlet_pids_map["ns-d"], 1, nf), np.zeros((2, 2)), decimal=5, ) assert_almost_equal( - ad.gamma_ns_qed((1, 1), br.non_singlet_pids_map["ns+u"], 1, nf)[0, 1], + ad_us.gamma_ns_qed((1, 1), br.non_singlet_pids_map["ns+u"], 1, nf)[0, 1], 0, decimal=5, ) assert_almost_equal( - ad.gamma_ns_qed((1, 1), br.non_singlet_pids_map["ns+d"], 1, nf)[0, 1], + ad_us.gamma_ns_qed((1, 1), br.non_singlet_pids_map["ns+d"], 1, nf)[0, 1], 0, decimal=5, ) # as1aem1 assert_almost_equal( - ad.gamma_ns_qed((1, 2), br.non_singlet_pids_map["ns-u"], 1, nf), + ad_us.gamma_ns_qed((1, 2), br.non_singlet_pids_map["ns-u"], 1, nf), np.zeros((2, 3)), decimal=5, ) assert_almost_equal( - ad.gamma_ns_qed((1, 2), br.non_singlet_pids_map["ns-d"], 1, nf), + ad_us.gamma_ns_qed((1, 2), br.non_singlet_pids_map["ns-d"], 1, nf), np.zeros((2, 3)), decimal=5, ) # aem2 assert_almost_equal( - ad.gamma_ns_qed((1, 2), br.non_singlet_pids_map["ns-u"], 1, nf), + ad_us.gamma_ns_qed((1, 2), br.non_singlet_pids_map["ns-u"], 1, nf), np.zeros((2, 3)), decimal=5, ) assert_almost_equal( - ad.gamma_ns_qed((1, 2), br.non_singlet_pids_map["ns-d"], 1, nf), + ad_us.gamma_ns_qed((1, 2), br.non_singlet_pids_map["ns-d"], 1, nf), np.zeros((2, 3)), decimal=5, ) - # as2 + # ad_us.as2 assert_almost_equal( - ad.gamma_ns_qed((2, 1), br.non_singlet_pids_map["ns-u"], 1, nf), + ad_us.gamma_ns_qed((2, 1), br.non_singlet_pids_map["ns-u"], 1, nf), np.zeros((3, 2)), decimal=5, ) assert_almost_equal( - ad.gamma_ns_qed((2, 1), br.non_singlet_pids_map["ns-d"], 1, nf), + ad_us.gamma_ns_qed((2, 1), br.non_singlet_pids_map["ns-d"], 1, nf), np.zeros((3, 2)), decimal=5, ) - # as3 + # ad_us.as3 assert_almost_equal( - ad.gamma_ns_qed((3, 1), br.non_singlet_pids_map["ns-u"], 1, nf), + ad_us.gamma_ns_qed((3, 1), br.non_singlet_pids_map["ns-u"], 1, nf), np.zeros((4, 2)), decimal=3, ) assert_almost_equal( - ad.gamma_ns_qed((3, 1), br.non_singlet_pids_map["ns-d"], 1, nf), + ad_us.gamma_ns_qed((3, 1), br.non_singlet_pids_map["ns-d"], 1, nf), np.zeros((4, 2)), decimal=3, ) @@ -197,13 +196,13 @@ def test_dim_singlet(): nf = 3 N = 2 sx = harmonics.sx(N, max_weight=3 + 1) - gamma_singlet = ad.gamma_singlet_qed((3, 2), N, nf) + gamma_singlet = ad_us.gamma_singlet_qed((3, 2), N, nf) assert gamma_singlet.shape == (4, 3, 4, 4) - gamma_singlet_as1 = as1.gamma_singlet_qed(N, sx[0], nf) + gamma_singlet_as1 = ad_us.as1.gamma_singlet_qed(N, sx[0], nf) assert gamma_singlet_as1.shape == (4, 4) - gamma_singlet_as2 = as2.gamma_singlet_qed(N, nf, sx) + gamma_singlet_as2 = ad_us.as2.gamma_singlet_qed(N, nf, sx) assert gamma_singlet_as2.shape == (4, 4) - gamma_singlet_as3 = as3.gamma_singlet_qed(N, nf, sx) + gamma_singlet_as3 = ad_us.as3.gamma_singlet_qed(N, nf, sx) assert gamma_singlet_as3.shape == (4, 4) @@ -211,13 +210,13 @@ def test_dim_valence(): nf = 3 N = 2 sx = harmonics.sx(N, max_weight=3 + 1) - gamma_valence = ad.gamma_valence_qed((3, 2), N, nf) + gamma_valence = ad_us.gamma_valence_qed((3, 2), N, nf) assert gamma_valence.shape == (4, 3, 2, 2) - gamma_valence_as1 = as1.gamma_valence_qed(N, sx[0]) + gamma_valence_as1 = ad_us.as1.gamma_valence_qed(N, sx[0]) assert gamma_valence_as1.shape == (2, 2) - gamma_valence_as2 = as2.gamma_valence_qed(N, nf, sx) + gamma_valence_as2 = ad_us.as2.gamma_valence_qed(N, nf, sx) assert gamma_valence_as2.shape == (2, 2) - gamma_valence_as3 = as3.gamma_valence_qed(N, nf, sx) + gamma_valence_as3 = ad_us.as3.gamma_valence_qed(N, nf, sx) assert gamma_valence_as3.shape == (2, 2) @@ -225,9 +224,9 @@ def test_dim_nsp(): nf = 3 N = 2 sx = harmonics.sx(N, max_weight=3 + 1) - gamma_nsup = ad.gamma_ns_qed((3, 2), 10102, N, nf) + gamma_nsup = ad_us.gamma_ns_qed((3, 2), 10102, N, nf) assert gamma_nsup.shape == (4, 3) - gamma_nsdp = ad.gamma_ns_qed((3, 2), 10103, N, nf) + gamma_nsdp = ad_us.gamma_ns_qed((3, 2), 10103, N, nf) assert gamma_nsdp.shape == (4, 3) with pytest.raises(NotImplementedError): - gamma_ns = ad.gamma_ns_qed((2, 0), 10106, N, nf) + gamma_ns = ad_us.gamma_ns_qed((2, 0), 10106, N, nf) From a2b3b1ebad532cb5f4b7e12ebb66ad6e6f251e43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Sun, 19 Feb 2023 18:41:26 +0100 Subject: [PATCH 287/312] Fix tests again --- tests/eko/kernels/test_kernels_QEDns.py | 4 ++-- tests/eko/test_couplings.py | 7 ++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/tests/eko/kernels/test_kernels_QEDns.py b/tests/eko/kernels/test_kernels_QEDns.py index f9c9ac24b..859efac3e 100644 --- a/tests/eko/kernels/test_kernels_QEDns.py +++ b/tests/eko/kernels/test_kernels_QEDns.py @@ -159,7 +159,7 @@ def test_ode(): evmod, masses, hqm_scheme=QuarkMassSchemes.POLE, - thresholds_ratios=MatchingScales(c=1.0, b=1.0, t=1.0), + thresholds_ratios=None, ) a0 = sc.a_s(mu2_0) for mu2_to in [10**2, 15**2]: @@ -244,7 +244,7 @@ def test_ode_true_gamma(): evmod, masses, hqm_scheme=QuarkMassSchemes.POLE, - thresholds_ratios=MatchingScales(c=1.0, b=1.0, t=1.0), + thresholds_ratios=None, ) a0 = sc.a_s(mu2_0) for mu2_to in [10**2, 15**2]: diff --git a/tests/eko/test_couplings.py b/tests/eko/test_couplings.py index 721859b51..f517e8249 100644 --- a/tests/eko/test_couplings.py +++ b/tests/eko/test_couplings.py @@ -237,16 +237,13 @@ def test_exact(self): max_num_flavs=6, ) ) - threshs = MatchingScales.from_dict( - dict(zip("cbt", thresh_setup)) - ) sc_expanded = Couplings( couplings, pto, method=CouplingEvolutionMethod.EXPANDED, masses=masses, hqm_scheme=QuarkMassSchemes.POLE, - thresholds_ratios=threshs, + thresholds_ratios=thresh_setup, ) sc_exact = Couplings( couplings, @@ -254,7 +251,7 @@ def test_exact(self): method=CouplingEvolutionMethod.EXACT, masses=masses, hqm_scheme=QuarkMassSchemes.POLE, - thresholds_ratios=threshs, + thresholds_ratios=thresh_setup, ) if pto in [(1, 0), (1, 1), (1, 2)]: precisions = (5e-4, 5e-4) From e09717886b04b42a8a9c031e22fdac779a2c5d90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 22 Feb 2023 14:43:05 +0100 Subject: [PATCH 288/312] Add case dim=2 to exp_matrix --- src/ekore/anomalous_dimensions/__init__.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/ekore/anomalous_dimensions/__init__.py b/src/ekore/anomalous_dimensions/__init__.py index e1f1a417f..2e1c0389f 100644 --- a/src/ekore/anomalous_dimensions/__init__.py +++ b/src/ekore/anomalous_dimensions/__init__.py @@ -81,10 +81,15 @@ def exp_matrix(gamma): e : numpy.ndarray projectors on the eigenspaces of the matrix gamma :math:`\gamma(N)` """ - w, v = np.linalg.eig(gamma) - v_inv = np.linalg.inv(v) dim = gamma.shape[0] e = np.zeros((dim, dim, dim), np.complex_) + if dim == 2: + exp, lambda_p, lambda_m, e_p, e_m = exp_matrix_2D(gamma) + e[0] = e_p + e[1] = e_m + return exp, np.array([lambda_p, lambda_m]), e + w, v = np.linalg.eig(gamma) + v_inv = np.linalg.inv(v) exp = np.zeros((dim, dim), np.complex_) for i in range(dim): e[i] = np.outer(v[:, i], v_inv[i]) From d5523e901ee8ace35b47b6ff04e9b88a831f3708 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Wed, 22 Feb 2023 14:59:27 +0100 Subject: [PATCH 289/312] Fix test ekore/anomalous_dimensions/unpolarized/space_like/test_init.py --- .../unpolarized/space_like/test_init.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/ekore/anomalous_dimensions/unpolarized/space_like/test_init.py b/tests/ekore/anomalous_dimensions/unpolarized/space_like/test_init.py index 219a225c6..6b1e74284 100644 --- a/tests/ekore/anomalous_dimensions/unpolarized/space_like/test_init.py +++ b/tests/ekore/anomalous_dimensions/unpolarized/space_like/test_init.py @@ -47,10 +47,10 @@ def test_exp_matrix(): res = expm(gamma_S_0_qed) res2 = ad.exp_matrix(gamma_S_0_qed)[0] assert_allclose(res, res2) - gamma_v_0_qed = ad_us.as1.gamma_valence_qed(3, s1) - res = expm(gamma_v_0_qed) - res2 = ad.exp_matrix(gamma_v_0_qed)[0] - assert_allclose(res, res2) + # gamma_v_0_qed = ad_us.as1.gamma_valence_qed(3, s1) + # res = expm(gamma_v_0_qed) + # res2 = ad.exp_matrix(gamma_v_0_qed)[0] + # assert_allclose(res, res2) diag = np.diag([1, 2, 3, 4]) assert_allclose(np.diag(np.exp([1, 2, 3, 4])), ad.exp_matrix(diag)[0]) id_ = np.identity(4, np.complex_) From 8d5a1ebf37f00a66113565715530c89868fd9a15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 23 Feb 2023 10:56:43 +0100 Subject: [PATCH 290/312] Simplify non_singlet_qed running alpha --- src/eko/kernels/non_singlet_qed.py | 18 ++++-------------- tests/eko/kernels/test_kernels_QEDns.py | 4 ++-- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/src/eko/kernels/non_singlet_qed.py b/src/eko/kernels/non_singlet_qed.py index 69346e79a..ea5a302d6 100644 --- a/src/eko/kernels/non_singlet_qed.py +++ b/src/eko/kernels/non_singlet_qed.py @@ -246,23 +246,13 @@ def running_alphaem_exact( non-singlet EKO """ a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) + mu2_steps = np.linspace(mu2_from, mu2_to, 1 + ev_op_iterations) res = 1.0 - betalist = [beta.beta_qcd((2 + i, 0), nf) for i in range(order[0])] - # For the moment implemented in this way to make numba compile it - # TODO : implement it with np.prod in a way that numba compiles it for step in range(1, ev_op_iterations + 1): aem = aem_list[step - 1] a1 = a_steps[step] a0 = a_steps[step - 1] - betalist[0] += aem * beta.beta_qcd((2, 1), nf) - gamma_ns_list = contract_gammas(gamma_ns, aem) - if order[0] == 1: - res *= as1_exact(gamma_ns_list[1:], a1, a0, betalist) - elif order[0] == 2: - res *= as2_exact(gamma_ns_list[1:], a1, a0, betalist) - elif order[0] == 3: - res *= as3_exact(gamma_ns_list[1:], a1, a0, betalist) - else: - raise NotImplementedError("Selected order is not implemented") - res *= apply_qed(gamma_ns_list[0], mu2_from, mu2_to) + mu2_from = mu2_steps[step - 1] + mu2_to = mu2_steps[step] + res *= fixed_alphaem_exact(order, gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from) return res diff --git a/tests/eko/kernels/test_kernels_QEDns.py b/tests/eko/kernels/test_kernels_QEDns.py index 859efac3e..e38c009ab 100644 --- a/tests/eko/kernels/test_kernels_QEDns.py +++ b/tests/eko/kernels/test_kernels_QEDns.py @@ -159,7 +159,7 @@ def test_ode(): evmod, masses, hqm_scheme=QuarkMassSchemes.POLE, - thresholds_ratios=None, + thresholds_ratios=[1.0, 1.0, 1.0], ) a0 = sc.a_s(mu2_0) for mu2_to in [10**2, 15**2]: @@ -244,7 +244,7 @@ def test_ode_true_gamma(): evmod, masses, hqm_scheme=QuarkMassSchemes.POLE, - thresholds_ratios=None, + thresholds_ratios=[1.0, 1.0, 1.0], ) a0 = sc.a_s(mu2_0) for mu2_to in [10**2, 15**2]: From dc5a2fb9437dce811815fc7c5fcbfef4f3b1dee1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 23 Feb 2023 11:10:19 +0100 Subject: [PATCH 291/312] Change test for 2D matrix --- tests/eko/kernels/test_kernels_QEDvalence.py | 2 -- .../unpolarized/space_like/test_init.py | 8 ++++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/eko/kernels/test_kernels_QEDvalence.py b/tests/eko/kernels/test_kernels_QEDvalence.py index be0474246..5c9780b04 100644 --- a/tests/eko/kernels/test_kernels_QEDvalence.py +++ b/tests/eko/kernels/test_kernels_QEDvalence.py @@ -4,8 +4,6 @@ import pytest from eko.kernels import valence_qed as val - -# from ekore.anomalous_dimensions.unpolarized import space_like as ad from ekore import anomalous_dimensions methods = [ diff --git a/tests/ekore/anomalous_dimensions/unpolarized/space_like/test_init.py b/tests/ekore/anomalous_dimensions/unpolarized/space_like/test_init.py index 6b1e74284..b83d80fa8 100644 --- a/tests/ekore/anomalous_dimensions/unpolarized/space_like/test_init.py +++ b/tests/ekore/anomalous_dimensions/unpolarized/space_like/test_init.py @@ -47,10 +47,10 @@ def test_exp_matrix(): res = expm(gamma_S_0_qed) res2 = ad.exp_matrix(gamma_S_0_qed)[0] assert_allclose(res, res2) - # gamma_v_0_qed = ad_us.as1.gamma_valence_qed(3, s1) - # res = expm(gamma_v_0_qed) - # res2 = ad.exp_matrix(gamma_v_0_qed)[0] - # assert_allclose(res, res2) + gamma_2D = np.random.rand(2, 2) + np.random.rand(2, 2) * 1j + res1 = expm(gamma_2D) + res2 = ad.exp_matrix(gamma_2D)[0] + assert_almost_equal(res1, res2) diag = np.diag([1, 2, 3, 4]) assert_allclose(np.diag(np.exp([1, 2, 3, 4])), ad.exp_matrix(diag)[0]) id_ = np.identity(4, np.complex_) From 25e97d477ec88a2a75d56699724ec0193e11ce81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 23 Feb 2023 15:00:24 +0100 Subject: [PATCH 292/312] Restructure function compute_aem_list --- benchmarks/NNPDF_bench.py | 6 +- benchmarks/apfel_bench.py | 6 +- src/eko/couplings.py | 248 ++++--------------------- src/eko/evolution_operator/__init__.py | 37 ++-- 4 files changed, 63 insertions(+), 234 deletions(-) diff --git a/benchmarks/NNPDF_bench.py b/benchmarks/NNPDF_bench.py index 9ec16c1e7..5de95ceeb 100644 --- a/benchmarks/NNPDF_bench.py +++ b/benchmarks/NNPDF_bench.py @@ -158,5 +158,7 @@ def benchmark(self, Q0=1.65, Q2grid=(100,)): # nn40 = BenchmarkNNPDF40() # # nn40.benchmark_nnlo(Q2grid=[100]) # nn40.benchmark_nnlo(Q0=np.sqrt(high2), Q2grid=[low2]) - nnpol = BenchmarkNNPDFpol11() - nnpol.benchmark(Q0=np.sqrt(low2), Q2grid=[high2]) + # nnpol = BenchmarkNNPDFpol11() + # nnpol.benchmark(Q0=np.sqrt(low2), Q2grid=[high2]) + obj = BenchmarkNNPDF31_luxqed() + obj.benchmark_nnlo(Q0=5.0) diff --git a/benchmarks/apfel_bench.py b/benchmarks/apfel_bench.py index 66358e209..96251a283 100644 --- a/benchmarks/apfel_bench.py +++ b/benchmarks/apfel_bench.py @@ -320,14 +320,14 @@ def benchmark_plain(self, pto, qed): if __name__ == "__main__": # obj = BenchmarkVFNS() - obj = BenchmarkFFNS() + # obj = BenchmarkFFNS() - obj.benchmark_plain_pol(2) + # obj.benchmark_plain_pol(2) # obj.benchmark_plain(2) # obj.benchmark_sv(2, "exponentiated") # obj.benchmark_kthr(2) # obj.benchmark_msbar(2) # obj = BenchmarkFFNS_qed() - obj = BenchmarkVFNS_qed() + obj = BenchmarkFFNS_qed() obj.benchmark_plain(2, 2) diff --git a/src/eko/couplings.py b/src/eko/couplings.py index 8d0924d44..783444333 100644 --- a/src/eko/couplings.py +++ b/src/eko/couplings.py @@ -285,7 +285,9 @@ def expanded_qed(ref, order, nf, lmu): @nb.njit(cache=True) -def couplings_expanded_alphaem_running(order, couplings_ref, nf, scale_from, scale_to): +def couplings_expanded_alphaem_running( + order, couplings_ref, nf, scale_from, scale_to, decoupled_running +): r"""Compute coupled expanded expression of the couplings for running alphaem. Implement Eqs. (17-18) from :cite:`Surguladze:1996hx` @@ -314,21 +316,20 @@ def couplings_expanded_alphaem_running(order, couplings_ref, nf, scale_from, sca res_aem = expanded_qed(couplings_ref[1], order[1], nf, lmu) # if order[0] >= 1 and order[1] >= 1: # order[0] is always >=1 - # TODO : implement decoupled running - # if not decoupled_running - if order[1] >= 1: - beta_qcd0 = beta_qcd((2, 0), nf) - beta_qed0 = beta_qed((0, 2), nf) - res_as += ( - -couplings_ref[0] ** 2 - * b_qcd((2, 1), nf) - * np.log(1 + beta_qcd0 * couplings_ref[1] * lmu) - ) - res_aem += ( - -couplings_ref[1] ** 2 - * b_qed((1, 2), nf) - * np.log(1 + beta_qed0 * couplings_ref[0] * lmu) - ) + if not decoupled_running: + if order[1] >= 1: + beta_qcd0 = beta_qcd((2, 0), nf) + beta_qed0 = beta_qed((0, 2), nf) + res_as += ( + -couplings_ref[0] ** 2 + * b_qcd((2, 1), nf) + * np.log(1 + beta_qcd0 * couplings_ref[1] * lmu) + ) + res_aem += ( + -couplings_ref[1] ** 2 + * b_qed((1, 2), nf) + * np.log(1 + beta_qed0 * couplings_ref[0] * lmu) + ) return np.array([res_as, res_aem]) @@ -456,6 +457,7 @@ def assert_positive(name, var): max_nf = couplings.max_num_flavs scheme_name = hqm_scheme.name self.alphaem_running = False if isnan(couplings.alphaem.scale) else True + self.decoupled_running = False # create new threshold object self.a_ref = np.array(couplings.values) / 4.0 / np.pi # convert to a_s and a_em @@ -468,17 +470,22 @@ def assert_positive(name, var): ) self.hqm_scheme = scheme_name logger.info( - "Strong Coupling: a_s(µ_R^2=%f)%s=%f=%f/(4Ï€)\n" - "Electromagnetic Coupling: a_em(µ_R^2=%f)%s=%f=%f/(4Ï€)", + "Strong Coupling: a_s(µ_R^2=%f)%s=%f=%f/(4Ï€)", self.q2_ref, f"^(nf={nf_ref})" if nf_ref else "", self.a_ref[0], self.a_ref[0] * 4 * np.pi, - self.q2_ref, - f"^(nf={nf_ref})" if nf_ref else "", - self.a_ref[1], - self.a_ref[1] * 4 * np.pi, ) + if self.order[1] > 0: + logger.info( + "Electromagnetic Coupling: a_em(µ_R^2=%f)%s=%f=%f/(4Ï€)\nalphaem running: %r\ndecoupled running: %r", + self.q2_ref, + f"^(nf={nf_ref})" if nf_ref else "", + self.a_ref[1], + self.a_ref[1] * 4 * np.pi, + self.alphaem_running, + self.decoupled_running, + ) # cache self.cache = {} @@ -579,8 +586,9 @@ def compute_exact_alphaem_running(self, a_ref, nf, scale_from, scale_to): return np.array([rge_qcd, a_ref[1]]) if self.order[1] >= 1: beta_qed_vec = [beta_qed((0, 2), nf)] - beta_qcd_mix = beta_qcd((2, 1), nf) - beta_qed_mix = beta_qed((1, 2), nf) # order[0] is always at least 1 + if not self.decoupled_running: + beta_qcd_mix = beta_qcd((2, 1), nf) + beta_qed_mix = beta_qed((1, 2), nf) # order[0] is always at least 1 if self.order[1] >= 2: beta_qed_vec.append(beta_qed((0, 3), nf)) @@ -657,191 +665,6 @@ def compute_exact_fixed_alphaem(self, a_ref, nf, scale_from, scale_to): ) return np.array([rge_qcd, a_ref[1]]) - # TODO : implement decoupled running - # def compute_exact_decoupled_running(self, a_ref, nf, scale_qcd_from, scale_qed_from, scale_to): - # """Compute couplings via |RGE| with running alphaem without the mixed terms. - - # Parameters - # ---------- - # as_ref : numpy.ndarray - # reference alpha_s and alpha - # nf : int - # value of nf for computing alpha_i - # scale_from : float - # reference scale - # scale_to : float - # target scale - - # Returns - # ------- - # numpy.ndarray - # couplings at target scale :math:`a(Q^2)` - # """ - # # when we discard the mixed terms the Qref doesn't have to be the same - # u_qcd = np.log(scale_to / scale_qcd_from) - # u_qed = np.log(scale_to / scale_qed_from) - - # # in LO fallback to expanded, as this is the full solution - # if self.order == (1, 0): - # return couplings_expanded_fixed_alphaem( - # self.order, a_ref, nf, scale_qcd_from, float(scale_to) - # ) - - # beta_qcd_vec = [beta_qcd((2, 0), nf)] - # # NLO - # if self.order[0] >= 2: - # beta_qcd_vec.append(beta_qcd((3, 0), nf)) - # # NNLO - # if self.order[0] >= 3: - # beta_qcd_vec.append(beta_qcd((4, 0), nf)) - # # N3LO - # if self.order[0] >= 4: - # beta_qcd_vec.append(beta_qcd((5, 0), nf)) - # b_qcd_vec = [ - # beta_qcd_vec[i] / beta_qcd_vec[0] for i in range(self.order[0]) - # ] - # a_s = self.unidimensional_exact( - # beta_qcd_vec[0], - # b_qcd_vec, - # u_qcd, - # a_ref[0], - # method="Radau", - # rtol=1e-6, - # ) - # if self.order[1] == 0: - # # for order = (qcd, 0) with qcd > 1 we return the exact solution for the QCD RGE - # # while aem is constant - # return np.array([a_s, a_ref[1]]) - # if self.order[1] >= 1: - # beta_qed_vec = [beta_qed((0, 2), nf)] - # if self.order[1] >= 2: - # beta_qed_vec.append(beta_qed((0, 3), nf)) - # b_qed_vec = [ - # beta_qed_vec[i] / beta_qed_vec[0] for i in range(self.order[1]) - # ] - # a_em = self.unidimensional_exact( - # beta_qed_vec[0], - # b_qed_vec, - # u_qed, - # a_ref[1], - # method="Radau", - # rtol=1e-6, - # ) - # return np.array([a_s, a_em]) - - def compute_aem_as(self, aem_ref, as_from, as_to, nf): - """Compute :math:`a_{em}` as a function of :math:`a_s`. - - Parameters - ---------- - as_to : float - target a_s - nf : int - value of nf for computing alpha_i - - Returns - ------- - float - a_em at target a_s :math:`a_em(a_s)` - """ - if not self.alphaem_running: - return self.a_ref[1] - beta_qcd_vec = [beta_qcd((2, 0), nf)] - beta_qed_vec = [] - beta_qcd_mix = 0 - beta_qed_mix = 0 - # NLO - if self.order[0] >= 2: - beta_qcd_vec.append(beta_qcd((3, 0), nf)) - # NNLO - if self.order[0] >= 3: - beta_qcd_vec.append(beta_qcd((4, 0), nf)) - # N3LO - if self.order[0] >= 4: - beta_qcd_vec.append(beta_qcd((5, 0), nf)) - if self.order[1] >= 1: - beta_qed_vec = [beta_qed((0, 2), nf)] - beta_qcd_mix = beta_qcd((2, 1), nf) - beta_qed_mix = beta_qed((1, 2), nf) # order[0] is always at least 1 - if self.order[1] >= 2: - beta_qed_vec.append(beta_qed((0, 3), nf)) - - def rge(_as, a_em, beta_qcd_vec, beta_qcd_mix, beta_qed_vec, beta_qed_mix): - rge_qed = -(a_em**2) * ( - np.sum([a_em**k * b for k, b in enumerate(beta_qed_vec)]) - + beta_qed_mix * _as - ) - rge_qcd = -(_as**2) * ( - np.sum([_as**k * b for k, b in enumerate(beta_qcd_vec)]) - + beta_qcd_mix * a_em - ) - - return rge_qed / rge_qcd - - res = scipy.integrate.solve_ivp( - rge, - (as_from, as_to), - (aem_ref,), - args=[beta_qcd_vec, beta_qcd_mix, beta_qed_vec, beta_qed_mix], - method="Radau", - rtol=1e-6, - ) - return res.y[0][-1] - - # TODO : implement decoupled running - # def compute_aem_as_decoupled(self, aem_ref, as_from, as_to, nf): - # """Compute :math:`a_{em}` as a function of :math:`a_s` with no mixing terms. - - # Parameters - # ---------- - # as_to : float - # target a_s - # nf : int - # value of nf for computing alpha_i - - # Returns - # ------- - # float - # a_em at target a_s :math:`a_em(a_s)` - # """ - # if not self.alphaem_running: - # return self.a_ref[1] - # beta_qcd_vec = [beta_qcd((2, 0), nf)] - # beta_qed_vec = [] - # # NLO - # if self.order[0] >= 2: - # beta_qcd_vec.append(beta_qcd((3, 0), nf)) - # # NNLO - # if self.order[0] >= 3: - # beta_qcd_vec.append(beta_qcd((4, 0), nf)) - # # N3LO - # if self.order[0] >= 4: - # beta_qcd_vec.append(beta_qcd((5, 0), nf)) - # if self.order[1] >= 1: - # beta_qed_vec = [beta_qed((0, 2), nf)] - # if self.order[1] >= 2: - # beta_qed_vec.append(beta_qed((0, 3), nf)) - - # def rge(_as, a_em, beta_qcd_vec, beta_qed_vec): - # rge_qed = -(a_em**2) * ( - # np.sum([a_em**k * b for k, b in enumerate(beta_qed_vec)]) - # ) - # rge_qcd = -(_as**2) * ( - # np.sum([_as**k * b for k, b in enumerate(beta_qcd_vec)]) - # ) - - # return rge_qed / rge_qcd - - # res = scipy.integrate.solve_ivp( - # rge, - # (as_from, as_to), - # (aem_ref,), - # args=[beta_qcd_vec, beta_qed_vec], - # method="Radau", - # rtol=1e-6, - # ) - # return res.y[0][-1] - def compute(self, a_ref, nf, scale_from, scale_to): """Compute actual couplings. @@ -881,7 +704,12 @@ def compute(self, a_ref, nf, scale_from, scale_to): else: if self.alphaem_running: a_new = couplings_expanded_alphaem_running( - self.order, a_ref.astype(float), nf, scale_from, float(scale_to) + self.order, + a_ref.astype(float), + nf, + scale_from, + float(scale_to), + self.decoupled_running, ) else: a_new = couplings_expanded_fixed_alphaem( diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index fcdd7f6ae..d782f79ef 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -609,7 +609,7 @@ def __init__( self.alphaem_running = self.managers["couplings"].alphaem_running if self.log_label == "Evolution": self.a = self.compute_a() - self.aem_list_as = self.compute_aem_list_as() + self.aem_list = self.compute_aem_list() @property def n_pools(self): @@ -677,7 +677,7 @@ def a_em(self): """Return the computed values for :math:`a_{em}`.""" return (self.a[0][1], self.a[1][1]) - def compute_aem_list_as(self): + def compute_aem_list(self): """Return the list of the couplings for the different values of :math:`a_s`.""" if self.order[1] == 0: return np.array([]) @@ -688,27 +688,27 @@ def compute_aem_list_as(self): q2ref = self.managers["couplings"].q2_ref delta_from = abs(self.q2_from - q2ref) delta_to = abs(self.q2_to - q2ref) - # I compute the values in aem_list_as starting from the as - # that is closer to as_ref. + # I compute the values in aem_list starting from the mu2 + # that is closer to mu_ref. if delta_from > delta_to: - as_start = as1 - aem_start = aem1 + a_start = np.array([as1, aem1]) + mu2_start = self.q2_to else: - as_start = as0 - aem_start = aem0 + a_start = np.array([as0, aem0]) + mu2_start = self.q2_from sc = self.managers["couplings"] ev_op_iterations = self.config["ev_op_iterations"] - as_steps = utils.geomspace(as0, as1, 1 + ev_op_iterations) - as_l = as_steps[0] + mu2_steps = np.linspace(self.q2_from, self.q2_to, 1 + ev_op_iterations) + mu2_l = mu2_steps[0] aem_list = [] - for as_h in as_steps[1:]: - as_half = (as_h + as_l) / 2.0 + for mu2_h in mu2_steps[1:]: + mu2_half = (mu2_h + mu2_l) / 2.0 aem_list.append( - sc.compute_aem_as( - aem_ref=aem_start, as_from=as_start, as_to=as_half, nf=self.nf - ) + sc.compute( + a_ref=a_start, nf=self.nf, scale_from=mu2_start, scale_to=mu2_half + )[1] ) - as_l = as_h + mu2_l = mu2_h return np.array(aem_list) @property @@ -786,7 +786,7 @@ def quad_ker(self, label, logx, areas): as0=self.a_s[0], mu2_from=self.q2_from, mu2_to=self.q2_to, - aem_list=self.aem_list_as, + aem_list=self.aem_list, alphaem_running=self.alphaem_running, nf=self.nf, L=np.log(self.xif2), @@ -909,11 +909,10 @@ def compute(self): ) if self.order[1] > 0: logger.info( - "%s: a_em distance: %e -> %e, running alphaem: %r", + "%s: a_em distance: %e -> %e", self.log_label, self.a_em[0], self.a_em[1], - self.managers["couplings"].alphaem_running, ) logger.info( "%s: order: (%d, %d), solution strategy: %s", From 429b24302ce7147be918eaf18bbf13396850631f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 23 Feb 2023 15:12:56 +0100 Subject: [PATCH 293/312] Fix FakeCoupling --- tests/eko/evolution_operator/test_init.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/eko/evolution_operator/test_init.py b/tests/eko/evolution_operator/test_init.py index 1127de1ba..b79965c46 100644 --- a/tests/eko/evolution_operator/test_init.py +++ b/tests/eko/evolution_operator/test_init.py @@ -196,8 +196,8 @@ def __init__(self): def a(self, scale_to=None, fact_scale=None, nf_to=None): return (0.1, 0.01) - def compute_aem_as(self, aem_ref, as_from, as_to, nf): - return aem_ref + def compute(self, a_ref, nf, scale_from, scale_to): + return a_ref fake_managers = {"couplings": FakeCoupling()} From 01af00e58af39d2bb843756f6253a2dc7b3f7121 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 23 Feb 2023 15:36:51 +0100 Subject: [PATCH 294/312] Clean couplings.py --- src/eko/couplings.py | 62 +++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 35 deletions(-) diff --git a/src/eko/couplings.py b/src/eko/couplings.py index 783444333..7b8df128d 100644 --- a/src/eko/couplings.py +++ b/src/eko/couplings.py @@ -206,7 +206,7 @@ def expanded_n3lo(ref, beta0, b1, b2, b3, lmu): @nb.njit(cache=True) -def expanded_qcd(ref, order, nf, lmu): +def expanded_qcd(ref, order, beta0, b_vec, lmu): r"""Compute QCD expanded solution at a given order. Parameters @@ -226,33 +226,30 @@ def expanded_qcd(ref, order, nf, lmu): strong coupling at target scale :math:`a_s(\mu_R^2)` """ res_as = ref - beta_vec = [beta_qcd((2, 0), nf)] - for i in range(2, order + 1): - beta_vec.append(b_qcd((i + 1, 0), nf)) # LO if order == 1: - res_as = exact_lo(ref, beta_vec[0], lmu) + res_as = exact_lo(ref, beta0, lmu) # NLO if order == 2: - res_as = expanded_nlo(ref, beta_vec[0], beta_vec[1], lmu) + res_as = expanded_nlo(ref, beta0, b_vec[1], lmu) # NNLO if order == 3: - res_as = expanded_nnlo(ref, beta_vec[0], beta_vec[1], beta_vec[2], lmu) + res_as = expanded_nnlo(ref, beta0, b_vec[1], b_vec[2], lmu) # N3LO if order == 4: res_as = expanded_n3lo( ref, - beta_vec[0], - beta_vec[1], - beta_vec[2], - beta_vec[3], + beta0, + b_vec[1], + b_vec[2], + b_vec[3], lmu, ) return res_as @nb.njit(cache=True) -def expanded_qed(ref, order, nf, lmu): +def expanded_qed(ref, order, beta0, b_vec, lmu): r"""Compute QED expanded solution at a given order. Parameters @@ -272,15 +269,12 @@ def expanded_qed(ref, order, nf, lmu): QED coupling at target scale :math:`a_em(\mu_R^2)` """ res_aem = ref - beta_vec = [beta_qed((0, 2), nf)] - for i in range(2, order + 1): - beta_vec.append(b_qed((0, i + 1), nf)) # LO if order == 1: - res_aem = exact_lo(ref, beta_vec[0], lmu) + res_aem = exact_lo(ref, beta0, lmu) # NLO if order == 2: - res_aem = expanded_nlo(ref, beta_vec[0], beta_vec[1], lmu) + res_aem = expanded_nlo(ref, beta0, b_vec[1], lmu) return res_aem @@ -312,23 +306,25 @@ def couplings_expanded_alphaem_running( """ # common vars lmu = np.log(scale_to / scale_from) - res_as = expanded_qcd(couplings_ref[0], order[0], nf, lmu) - res_aem = expanded_qed(couplings_ref[1], order[1], nf, lmu) + beta0_qcd = beta_qcd((2, 0), nf) + b_vec_qcd = [b_qcd((i + 2, 0), nf) for i in range(order[0])] + res_as = expanded_qcd(couplings_ref[0], order[0], beta0_qcd, b_vec_qcd, lmu) + beta0_qed = beta_qed((0, 2), nf) + b_vec_qed = [b_qed((0, i + 2), nf) for i in range(order[1])] + res_aem = expanded_qed(couplings_ref[1], order[1], beta0_qed, b_vec_qed, lmu) # if order[0] >= 1 and order[1] >= 1: # order[0] is always >=1 if not decoupled_running: if order[1] >= 1: - beta_qcd0 = beta_qcd((2, 0), nf) - beta_qed0 = beta_qed((0, 2), nf) res_as += ( -couplings_ref[0] ** 2 * b_qcd((2, 1), nf) - * np.log(1 + beta_qcd0 * couplings_ref[1] * lmu) + * np.log(1 + beta0_qcd * couplings_ref[1] * lmu) ) res_aem += ( -couplings_ref[1] ** 2 * b_qed((1, 2), nf) - * np.log(1 + beta_qed0 * couplings_ref[0] * lmu) + * np.log(1 + beta0_qed * couplings_ref[0] * lmu) ) return np.array([res_as, res_aem]) @@ -362,28 +358,24 @@ def couplings_expanded_fixed_alphaem(order, couplings_ref, nf, scale_from, scale beta_qcd0 = beta_qcd((2, 0), nf) if order[1] >= 1: beta_qcd0 += aem * beta_qcd((2, 1), nf) - beta_vec = [beta_qcd0] - for i in range(2, order[0] + 1): - beta_vec.append(beta_qcd((i + 1, 0), nf) / beta_qcd0) + b_vec = [beta_qcd((i + 2, 0), nf) / beta_qcd0 for i in range(order[0])] # LO if order[0] == 1: - res_as = exact_lo(couplings_ref[0], beta_vec[0], lmu) + res_as = exact_lo(couplings_ref[0], beta_qcd0, lmu) # NLO if order[0] == 2: - res_as = expanded_nlo(couplings_ref[0], beta_vec[0], beta_vec[1], lmu) + res_as = expanded_nlo(couplings_ref[0], beta_qcd0, b_vec[1], lmu) # NNLO if order[0] == 3: - res_as = expanded_nnlo( - couplings_ref[0], beta_vec[0], beta_vec[1], beta_vec[2], lmu - ) + res_as = expanded_nnlo(couplings_ref[0], beta_qcd0, b_vec[1], b_vec[2], lmu) # N3LO if order[0] == 4: res_as = expanded_n3lo( couplings_ref[0], - beta_vec[0], - beta_vec[1], - beta_vec[2], - beta_vec[3], + beta_qcd0, + b_vec[1], + b_vec[2], + b_vec[3], lmu, ) return np.array([res_as, aem]) From 66cce5227d945d9a6c937792ed2ef9b786d4e611 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 23 Feb 2023 16:59:08 +0100 Subject: [PATCH 295/312] Restore expanded scale variations for qed --- src/eko/evolution_operator/__init__.py | 40 +++++++++--------- src/eko/scale_variations/expanded.py | 46 ++++----------------- tests/eko/scale_variations/test_expanded.py | 4 +- 3 files changed, 29 insertions(+), 61 deletions(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index d782f79ef..02159497d 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -392,7 +392,7 @@ def quad_ker_qcd( # scale var expanded is applied on the kernel if sv_mode == sv.Modes.expanded and not is_threshold: ker = np.ascontiguousarray( - sv.expanded.singlet_variation(gamma_singlet, as1, order, nf, L) + sv.expanded.singlet_variation(gamma_singlet, as1, order, nf, L, dim=2) ) @ np.ascontiguousarray(ker) ker = select_singlet_element(ker, mode0, mode1) else: @@ -502,12 +502,12 @@ def quad_ker_qed( ev_op_max_order, ) # scale var expanded is applied on the kernel - # if sv_mode == sv.Modes.expanded and not is_threshold: - # ker = np.ascontiguousarray(ker) @ np.ascontiguousarray( - # sv.expanded.singlet_variation_qed( - # gamma_s, as_raw, aem_list[-1], alphaem_running, order, nf, L - # ) - # ) + if sv_mode == sv.Modes.expanded and not is_threshold: + ker = np.ascontiguousarray( + sv.expanded.singlet_variation_qed( + gamma_s, as1, aem_list[-1], alphaem_running, order, nf, L + ) + ) @ np.ascontiguousarray(ker) ker = select_QEDsinglet_element(ker, mode0, mode1) elif ker_base.is_QEDvalence: gamma_v = ad_us.gamma_valence_qed(order, ker_base.n, nf) @@ -528,12 +528,12 @@ def quad_ker_qed( ev_op_max_order, ) # scale var expanded is applied on the kernel - # if sv_mode == sv.Modes.expanded and not is_threshold: - # ker = np.ascontiguousarray( - # sv.expanded.valence_variation_qed( - # gamma_v, as_raw, aem_list[-1], alphaem_running, order, nf, L - # ) - # ) @ np.ascontiguousarray(ker) + if sv_mode == sv.Modes.expanded and not is_threshold: + ker = np.ascontiguousarray( + sv.expanded.valence_variation_qed( + gamma_s, as1, aem_list[-1], alphaem_running, order, nf, L + ) + ) @ np.ascontiguousarray(ker) ker = select_QEDvalence_element(ker, mode0, mode1) else: gamma_ns = ad_us.gamma_ns_qed(order, mode0, ker_base.n, nf) @@ -555,13 +555,13 @@ def quad_ker_qed( mu2_from, mu2_to, ) - # if sv_mode == sv.Modes.expanded and not is_threshold: - # ker = ( - # sv.expanded.non_singlet_variation_qed( - # gamma_ns, as_raw, aem_list[-1], alphaem_running, order, nf, L - # ) - # * ker - # ) + if sv_mode == sv.Modes.expanded and not is_threshold: + ker = ( + sv.expanded.non_singlet_variation_qed( + gamma_ns, as1, aem_list[-1], alphaem_running, order, nf, L + ) + * ker + ) return ker diff --git a/src/eko/scale_variations/expanded.py b/src/eko/scale_variations/expanded.py index 802141140..08b752a09 100644 --- a/src/eko/scale_variations/expanded.py +++ b/src/eko/scale_variations/expanded.py @@ -135,7 +135,7 @@ def non_singlet_variation(gamma, a_s, order, nf, L): @nb.njit(cache=True) -def singlet_variation(gamma, a_s, order, nf, L): +def singlet_variation(gamma, a_s, order, nf, L, dim): """Singlet scale variation dispatcher. Parameters @@ -156,7 +156,7 @@ def singlet_variation(gamma, a_s, order, nf, L): numpy.ndarray scale variation kernel """ - sv_ker = np.eye(2, dtype=np.complex_) + sv_ker = np.eye(dim, dtype=np.complex_) gamma = np.ascontiguousarray(gamma) if order[0] >= 2: sv_ker -= a_s * variation_as1(gamma, L) @@ -201,7 +201,7 @@ def non_singlet_variation_qed(gamma, a_s, a_em, alphaem_running, order, nf, L): sv_ker = non_singlet_variation(gamma[1:, 0], a_s, order, nf, L) if alphaem_running: if order[1] >= 2: - sv_ker += a_em * variation_as1(gamma[0, 1:], L) + sv_ker -= a_em * variation_as1(gamma[0, 1:], L) return sv_ker @@ -227,26 +227,10 @@ def singlet_variation_qed(gamma, a_s, a_em, alphaem_running, order, nf, L): numpy.ndarray scale variation kernel """ - sv_ker = np.eye(4, dtype=np.complex_) - gamma = np.ascontiguousarray(gamma) - if order[0] >= 2: - sv_ker += a_s * variation_as1(gamma[1:, 0], L) - if order[0] >= 3: - beta0 = beta.beta_qcd_as2(nf) - gamma10e2 = gamma[1, 0] @ gamma[1, 0] - sv_ker += a_s**2 * variation_as2(gamma[1:, 0], L, beta0, gamma10e2) - if order[0] >= 4: - beta1 = beta.beta_qcd((3, 0), nf) - gamma10e3 = gamma10e2 @ gamma[1, 0] - # here the product is not commutative - g20g10 = gamma[2, 0] @ gamma[1, 0] - g10g20 = gamma[1, 0] @ gamma[2, 0] - sv_ker += a_s**3 * variation_as3( - gamma[1:, 0], L, beta0, beta1, gamma10e2, gamma10e3, g20g10, g10g20 - ) + sv_ker = singlet_variation(gamma[1:, 0], a_s, order, nf, L, 4) if alphaem_running: if order[1] >= 2: - sv_ker += a_em * variation_as1(gamma[0, 1:], L) + sv_ker -= a_em * variation_as1(gamma[0, 1:], L) return sv_ker @@ -272,24 +256,8 @@ def valence_variation_qed(gamma, a_s, a_em, alphaem_running, order, nf, L): numpy.ndarray scale variation kernel """ - sv_ker = np.eye(2, dtype=np.complex_) - gamma = np.ascontiguousarray(gamma) - if order[0] >= 2: - sv_ker += a_s * variation_as1(gamma[1:, 0], L) - if order[0] >= 3: - beta0 = beta.beta_qcd_as2(nf) - gamma10e2 = gamma[1, 0] @ gamma[1, 0] - sv_ker += a_s**2 * variation_as2(gamma[1:, 0], L, beta0, gamma10e2) - if order[0] >= 4: - beta1 = beta.beta_qcd((3, 0), nf) - gamma10e3 = gamma10e2 @ gamma[1, 0] - # here the product is not commutative - g20g10 = gamma[2, 0] @ gamma[1, 0] - g10g20 = gamma[1, 0] @ gamma[2, 0] - sv_ker += a_s**3 * variation_as3( - gamma[1:, 0], L, beta0, beta1, gamma10e2, gamma10e3, g20g10, g10g20 - ) + sv_ker = singlet_variation(gamma[1:, 0], a_s, order, nf, L, 2) if alphaem_running: if order[1] >= 2: - sv_ker += a_em * variation_as1(gamma[0, 1:], L) + sv_ker -= a_em * variation_as1(gamma[0, 1:], L) return sv_ker diff --git a/tests/eko/scale_variations/test_expanded.py b/tests/eko/scale_variations/test_expanded.py index 34be77043..b58a785b1 100644 --- a/tests/eko/scale_variations/test_expanded.py +++ b/tests/eko/scale_variations/test_expanded.py @@ -53,7 +53,7 @@ def test_singlet_sv_dispacher(): nf = 5 a_s = 0.35 np.testing.assert_allclose( - expanded.singlet_variation(gamma_singlet, a_s, order, nf, L), np.eye(2) + expanded.singlet_variation(gamma_singlet, a_s, order, nf, L, 2), np.eye(2) ) @@ -184,7 +184,7 @@ def scheme_diff(g, k, pto, is_singlet): ker_a = singlet.dispatcher( order, method, gs_a, a1, a0, nf, ev_op_iterations=1, ev_op_max_order=1 ) - ker_b = ker @ expanded.singlet_variation(gs, a1, order, nf, L) + ker_b = ker @ expanded.singlet_variation(gs, a1, order, nf, L, 2) s_diff = scheme_diff(gs, L, order, True) np.testing.assert_allclose( (ker_a - ker_b) @ np.linalg.inv(ker), From e21fbaf47e8f7d3690df7a9a75bcffe3ba57ddb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 23 Feb 2023 17:11:35 +0100 Subject: [PATCH 296/312] Fix typo --- src/eko/evolution_operator/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 02159497d..1e5ecee71 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -531,7 +531,7 @@ def quad_ker_qed( if sv_mode == sv.Modes.expanded and not is_threshold: ker = np.ascontiguousarray( sv.expanded.valence_variation_qed( - gamma_s, as1, aem_list[-1], alphaem_running, order, nf, L + gamma_v, as1, aem_list[-1], alphaem_running, order, nf, L ) ) @ np.ascontiguousarray(ker) ker = select_QEDvalence_element(ker, mode0, mode1) From 57f3356e20304afad1c922bfdb11f8ad88e4249a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 24 Feb 2023 12:29:18 +0100 Subject: [PATCH 297/312] Update docstrings in non_singlet --- src/eko/evolution_operator/__init__.py | 4 +- src/eko/kernels/non_singlet.py | 112 ++++++++++++------------- src/eko/kernels/non_singlet_qed.py | 30 ++++--- 3 files changed, 76 insertions(+), 70 deletions(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 1e5ecee71..c90581809 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -696,7 +696,7 @@ def compute_aem_list(self): else: a_start = np.array([as0, aem0]) mu2_start = self.q2_from - sc = self.managers["couplings"] + couplings = self.managers["couplings"] ev_op_iterations = self.config["ev_op_iterations"] mu2_steps = np.linspace(self.q2_from, self.q2_to, 1 + ev_op_iterations) mu2_l = mu2_steps[0] @@ -704,7 +704,7 @@ def compute_aem_list(self): for mu2_h in mu2_steps[1:]: mu2_half = (mu2_h + mu2_l) / 2.0 aem_list.append( - sc.compute( + couplings.compute( a_ref=a_start, nf=self.nf, scale_from=mu2_start, scale_to=mu2_half )[1] ) diff --git a/src/eko/kernels/non_singlet.py b/src/eko/kernels/non_singlet.py index 2b5d7c93f..24706a794 100644 --- a/src/eko/kernels/non_singlet.py +++ b/src/eko/kernels/non_singlet.py @@ -17,8 +17,8 @@ def U_vec(gamma_ns, beta, order): ---------- gamma_ns : numpy.ndarray non-singlet anomalous dimension - nf : int - number of active flavors + beta : list + list of the values of the beta functions order : int perturbative order @@ -53,19 +53,19 @@ def lo_exact(gamma_ns, a1, a0, beta): Parameters ---------- - gamma_ns : numpy.ndarray - non-singlet anomalous dimensions - a1 : float - target coupling value - a0 : float - initial coupling value - nf : int - number of active flavors + gamma_ns : numpy.ndarray + non-singlet anomalous dimensions + a1 : float + target coupling value + a0 : float + initial coupling value + beta : list + list of the values of the beta functions Returns ------- - e_ns^0 : complex - |LO| non-singlet exact EKO + e_ns^0 : complex + |LO| non-singlet exact EKO """ beta0 = beta[0] return np.exp(gamma_ns[0] * ei.j12(a1, a0, beta0)) @@ -77,19 +77,19 @@ def nlo_exact(gamma_ns, a1, a0, beta): Parameters ---------- - gamma_ns : numpy.ndarray - non-singlet anomalous dimensions - a1 : float - target coupling value - a0 : float - initial coupling value - nf : int - number of active flavors + gamma_ns : numpy.ndarray + non-singlet anomalous dimensions + a1 : float + target coupling value + a0 : float + initial coupling value + beta : list + list of the values of the beta functions Returns ------- - e_ns^1 : complex - |NLO| non-singlet exact EKO + e_ns^1 : complex + |NLO| non-singlet exact EKO """ beta0 = beta[0] b_vec = [betas / beta0 for betas in beta] @@ -105,19 +105,19 @@ def nlo_expanded(gamma_ns, a1, a0, beta): Parameters ---------- - gamma_ns : numpy.ndarray - non-singlet anomalous dimensions - a1 : float - target coupling value - a0 : float - initial coupling value - nf : int - number of active flavors + gamma_ns : numpy.ndarray + non-singlet anomalous dimensions + a1 : float + target coupling value + a0 : float + initial coupling value + beta : list + list of the values of the beta functions Returns ------- - e_ns^1 : complex - |NLO| non-singlet expanded EKO + e_ns^1 : complex + |NLO| non-singlet expanded EKO """ beta0 = beta[0] b_vec = [betas / beta0 for betas in beta] @@ -133,19 +133,19 @@ def nnlo_exact(gamma_ns, a1, a0, beta): Parameters ---------- - gamma_ns : numpy.ndarray - non-singlet anomalous dimensions - a1 : float - target coupling value - a0 : float - initial coupling value - nf : int - number of active flavors + gamma_ns : numpy.ndarray + non-singlet anomalous dimensions + a1 : float + target coupling value + a0 : float + initial coupling value + beta : list + list of the values of the beta functions Returns ------- - e_ns^2 : complex - |NNLO| non-singlet exact EKO + e_ns^2 : complex + |NNLO| non-singlet exact EKO """ beta0 = beta[0] b_vec = [betas / beta0 for betas in beta] @@ -162,19 +162,19 @@ def nnlo_expanded(gamma_ns, a1, a0, beta): Parameters ---------- - gamma_ns : numpy.ndarray - non-singlet anomalous dimensions - a1 : float - target coupling value - a0 : float - initial coupling value - nf : int - number of active flavors + gamma_ns : numpy.ndarray + non-singlet anomalous dimensions + a1 : float + target coupling value + a0 : float + initial coupling value + beta : list + list of the values of the beta functions Returns ------- - e_ns^2 : complex - |NNLO| non-singlet expanded EKO + e_ns^2 : complex + |NNLO| non-singlet expanded EKO """ beta0 = beta[0] b_vec = [betas / beta0 for betas in beta] @@ -276,8 +276,8 @@ def eko_ordered_truncated(gamma_ns, a1, a0, beta, order, ev_op_iterations): target coupling value a0 : float initial coupling value - nf : int - number of active flavors + beta : list + list of the values of the beta functions order : tuple(int,int) perturbative order ev_op_iterations : int @@ -316,8 +316,8 @@ def eko_truncated(gamma_ns, a1, a0, beta, order, ev_op_iterations): target coupling value a0 : float initial coupling value - nf : int - number of active flavors + beta : list + list of the values of the beta functions order : tuple(int,int) perturbative order ev_op_iterations : int diff --git a/src/eko/kernels/non_singlet_qed.py b/src/eko/kernels/non_singlet_qed.py index ea5a302d6..b92365c90 100644 --- a/src/eko/kernels/non_singlet_qed.py +++ b/src/eko/kernels/non_singlet_qed.py @@ -63,10 +63,8 @@ def as1_exact(gamma_ns, a1, a0, beta): target coupling value a0 : float initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors + beta : list + list of the values of the beta functions Returns ------- @@ -88,10 +86,8 @@ def as2_exact(gamma_ns, a1, a0, beta): target coupling value a0 : float initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors + beta : list + list of the values of the beta functions Returns ------- @@ -113,10 +109,8 @@ def as3_exact(gamma_ns, a1, a0, beta): target coupling value a0 : float initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors + beta : list + list of the values of the beta functions Returns ------- @@ -164,6 +158,10 @@ def dispatcher( number of active flavors ev_op_iterations : int number of evolution steps + mu2_from : float + initial value of mu2 + mu2_from : float + final value of mu2 Returns ------- @@ -197,6 +195,10 @@ def fixed_alphaem_exact(order, gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from): electromagnetic coupling value nf : int number of active flavors + mu2_from : float + initial value of mu2 + mu2_from : float + final value of mu2 Returns ------- @@ -239,6 +241,10 @@ def running_alphaem_exact( number of active flavors ev_op_iterations : int number of evolution steps + mu2_from : float + initial value of mu2 + mu2_from : float + final value of mu2 Returns ------- From bd9ef19fbcab37cfa72ba4a0f3e43c5b7dd0bc5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 24 Feb 2023 12:37:50 +0100 Subject: [PATCH 298/312] Update docstrings in 'singlet' sectors --- src/eko/kernels/singlet.py | 54 +++++++------------ src/eko/kernels/singlet_qed.py | 18 ++++--- src/eko/kernels/valence_qed.py | 95 ++++++++-------------------------- 3 files changed, 50 insertions(+), 117 deletions(-) diff --git a/src/eko/kernels/singlet.py b/src/eko/kernels/singlet.py index 87a19ff07..159135dc6 100644 --- a/src/eko/kernels/singlet.py +++ b/src/eko/kernels/singlet.py @@ -23,8 +23,8 @@ def lo_exact(gamma_singlet, a1, a0, beta): target coupling value a0 : float initial coupling value - nf : int - number of active flavors + beta : list + list of the values of the beta functions Returns ------- @@ -42,12 +42,6 @@ def nlo_decompose(gamma_singlet, j01, j11): ---------- gamma_singlet : numpy.ndarray singlet anomalous dimensions matrices - a1 : float - target coupling value - a0 : float - initial coupling value - nf : int - number of active flavors j01 : float |LO|-|NLO| evolution integral j11 : float @@ -73,8 +67,8 @@ def nlo_decompose_exact(gamma_singlet, a1, a0, beta): target coupling value a0 : float initial coupling value - nf : int - number of active flavors + beta : list + list of the values of the beta functions Returns ------- @@ -102,8 +96,8 @@ def nlo_decompose_expanded(gamma_singlet, a1, a0, beta): target coupling value a0 : float initial coupling value - nf : int - number of active flavors + beta : list + list of the values of the beta functions Returns ------- @@ -127,12 +121,6 @@ def nnlo_decompose(gamma_singlet, j02, j12, j22): ---------- gamma_singlet : numpy.ndarray singlet anomalous dimensions matrices - a1 : float - target coupling value - a0 : float - initial coupling value - nf : int - number of active flavors j02 : float LO-NNLO evolution integral j12 : float @@ -162,8 +150,8 @@ def nnlo_decompose_exact(gamma_singlet, a1, a0, beta): target coupling value a0 : float initial coupling value - nf : int - number of active flavors + beta : list + list of the values of the beta functions Returns ------- @@ -192,8 +180,8 @@ def nnlo_decompose_expanded(gamma_singlet, a1, a0, beta): target coupling value a0 : float initial coupling value - nf : int - number of active flavors + beta : list + list of the values of the beta functions Returns ------- @@ -218,12 +206,6 @@ def n3lo_decompose(gamma_singlet, j03, j13, j23, j33): ---------- gamma_singlet : numpy.ndarray singlet anomalous dimensions matrices - a1 : float - target coupling value - a0 : float - initial coupling value - nf : int - number of active flavors j03 : float |LO|-|N3LO| evolution integral j13 : float @@ -329,8 +311,8 @@ def eko_iterate(gamma_singlet, a1, a0, beta_vec, order, ev_op_iterations): target coupling value a0 : float initial coupling value - nf : int - number of active flavors + beta_vec : list + list of the values of the beta functions order : tuple(int,int) perturbative order ev_op_iterations : int @@ -371,8 +353,8 @@ def r_vec(gamma_singlet, beta, ev_op_max_order, order, is_exact): ---------- gamma_singlet : numpy.ndarray singlet anomalous dimensions matrices - nf : int - number of active flavors + beta : list + list of the values of the beta functions ev_op_max_order : tuple(int,int) perturbative expansion order of U order : tuple(int,int) @@ -498,8 +480,8 @@ def eko_perturbative( target coupling value a0 : float initial coupling value - nf : int - number of active flavors + beta : list + list of the values of the beta functions order : tuple(int,int) perturbative order ev_op_iterations : int @@ -543,8 +525,8 @@ def eko_truncated(gamma_singlet, a1, a0, beta, order, ev_op_iterations): target coupling value a0 : float initial coupling value - nf : int - number of active flavors + beta : list + list of the values of the beta functions order : tuple(int,int) perturbative order ev_op_iterations : int diff --git a/src/eko/kernels/singlet_qed.py b/src/eko/kernels/singlet_qed.py index 4fd48a4b7..52ce456e6 100644 --- a/src/eko/kernels/singlet_qed.py +++ b/src/eko/kernels/singlet_qed.py @@ -9,7 +9,7 @@ @nb.njit(cache=True) -def eko_iterate(gamma_singlet, a1, a0, aem_list, nf, order, ev_op_iterations): +def eko_iterate(gamma_singlet, a1, a0, aem_list, nf, order, ev_op_iterations, dim): """Singlet QEDxQCD iterated (exact) EKO. Parameters @@ -20,8 +20,8 @@ def eko_iterate(gamma_singlet, a1, a0, aem_list, nf, order, ev_op_iterations): target strong coupling value a0 : float initial strong coupling value - aem : float - electromagnetic coupling value + aem_list : float + electromagnetic coupling values nf : int number of active flavors order : tuple(int,int) @@ -35,7 +35,7 @@ def eko_iterate(gamma_singlet, a1, a0, aem_list, nf, order, ev_op_iterations): singlet QEDxQCD iterated (exact) EKO """ a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) - e = np.identity(4, np.complex_) + e = np.identity(dim, np.complex_) betaQCD = np.zeros((order[0] + 1, order[1] + 1)) for i in range(1, order[0] + 1): betaQCD[i, 0] = beta.beta_qcd((i + 1, 0), nf) @@ -45,7 +45,7 @@ def eko_iterate(gamma_singlet, a1, a0, aem_list, nf, order, ev_op_iterations): al = a_steps[step - 1] a_half = (ah + al) / 2.0 delta_a = ah - al - gamma = np.zeros((4, 4), np.complex_) + gamma = np.zeros((dim, dim), np.complex_) betatot = 0 for i in range(0, order[0] + 1): for j in range(0, order[1] + 1): @@ -83,8 +83,8 @@ def dispatcher( target coupling value a0 : float initial coupling value - aem : float - electromagnetic coupling value + aem_list : numpy.ndarray + electromagnetic coupling values nf : int number of active flavors ev_op_iterations : int @@ -98,5 +98,7 @@ def dispatcher( singlet EKO """ if method in ["iterate-exact", "iterate-expanded"]: - return eko_iterate(gamma_singlet, a1, a0, aem_list, nf, order, ev_op_iterations) + return eko_iterate( + gamma_singlet, a1, a0, aem_list, nf, order, ev_op_iterations, 4 + ) raise NotImplementedError('Only "iterate-exact" is implemented with QED') diff --git a/src/eko/kernels/valence_qed.py b/src/eko/kernels/valence_qed.py index 83d5d6844..5a11fd98c 100644 --- a/src/eko/kernels/valence_qed.py +++ b/src/eko/kernels/valence_qed.py @@ -4,58 +4,7 @@ from ekore import anomalous_dimensions as ad -from .. import beta -from . import utils - - -@nb.njit(cache=True) -def eko_iterate(gamma_valence, a1, a0, aem_list, nf, order, ev_op_iterations): - """ - Valence iterated (exact) EKO. - - Parameters - ---------- - gamma_valence : numpy.ndarray - valence anomalous dimensions matrices - a1 : float - target coupling value - a0 : float - initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors - order : int - perturbative order - ev_op_iterations : int - number of evolution steps - - Returns - ------- - e_v^{order} : numpy.ndarray - Valence iterated (exact) EKO - """ - a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) - e = np.identity(2, np.complex_) - betaQCD = np.zeros((order[0] + 1, order[1] + 1)) - for i in range(1, order[0] + 1): - betaQCD[i, 0] = beta.beta_qcd((i + 1, 0), nf) - betaQCD[1, 1] = beta.beta_qcd((2, 1), nf) - for step in range(1, ev_op_iterations + 1): - ah = a_steps[step] - al = a_steps[step - 1] - a_half = (ah + al) / 2.0 - delta_a = ah - al - gamma = np.zeros((2, 2), np.complex_) - betatot = 0 - for i in range(0, order[0] + 1): - for j in range(0, order[1] + 1): - betatot += betaQCD[i, j] * a_half ** (i + 1) * aem_list[step - 1] ** j - gamma += gamma_valence[i, j] * a_half**i * aem_list[step - 1] ** j - ln = gamma / betatot * delta_a - ek = np.ascontiguousarray(ad.exp_matrix_2D(ln)[0]) - e = ek @ e - return e +from .singlet_qed import eko_iterate @nb.njit(cache=True) @@ -73,28 +22,26 @@ def dispatcher( """ Determine used kernel and call it. - In LO we always use the exact solution. - Parameters ---------- - order : tuple(int,int) - perturbative order - method : str - method - gamma_singlet : numpy.ndarray - singlet anomalous dimensions matrices - a1 : float - target coupling value - a0 : float - initial coupling value - aem : float - electromagnetic coupling value - nf : int - number of active flavors - ev_op_iterations : int - number of evolution steps - ev_op_max_order : tuple(int,int) - perturbative expansion order of U + order : tuple(int,int) + perturbative order + method : str + method + gamma_singlet : numpy.ndarray + singlet anomalous dimensions matrices + a1 : float + target coupling value + a0 : float + initial coupling value + aem_list : numpy.ndarray + electromagnetic coupling values + nf : int + number of active flavors + ev_op_iterations : int + number of evolution steps + ev_op_max_order : tuple(int,int) + perturbative expansion order of U Returns ------- @@ -102,5 +49,7 @@ def dispatcher( singlet EKO """ if method in ["iterate-exact", "iterate-expanded"]: - return eko_iterate(gamma_valence, a1, a0, aem_list, nf, order, ev_op_iterations) + return eko_iterate( + gamma_valence, a1, a0, aem_list, nf, order, ev_op_iterations, 2 + ) raise NotImplementedError('Only "iterate-exact" is implemented with QED') From aa532e3fd62ebaf58506aa78e4df0979be8ab286 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 24 Feb 2023 12:48:44 +0100 Subject: [PATCH 299/312] Update docstring in couplings --- src/eko/couplings.py | 38 ++++++++++++++++++++++++---------- src/eko/kernels/valence_qed.py | 3 --- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/eko/couplings.py b/src/eko/couplings.py index 7b8df128d..24ba0de13 100644 --- a/src/eko/couplings.py +++ b/src/eko/couplings.py @@ -215,8 +215,10 @@ def expanded_qcd(ref, order, beta0, b_vec, lmu): reference value of the strong coupling order : int QCD order - nf : int - number of flavors + beta0 : float + first coefficient of the beta function + b_vec : list + list of b function coefficients (including b0) lmu : float logarithm of the ratio between target and reference scales @@ -258,8 +260,10 @@ def expanded_qed(ref, order, beta0, b_vec, lmu): reference value of the QED coupling order : int QED order - nf : int - number of flavors + beta0 : float + first coefficient of the beta function + b_vec : list + list of b function coefficients (including b0) lmu : float logarithm of the ratio between target and reference scales @@ -298,6 +302,8 @@ def couplings_expanded_alphaem_running( reference scale scale_to : float target scale + decoupled_running : bool + whether the running of the couplings is decoupled or not Returns ------- @@ -491,14 +497,18 @@ def unidimensional_exact(self, beta0, b_vec, u, a_ref, method, rtol): Parameters ---------- - as_ref : float + beta0 : float + first coefficient of the beta function + b_vec : list + list of b function coefficients (including b0) + u : float + :math:`log(scale_to / scale_from)` + a_ref : float reference alpha_s or alpha - nf : int - value of nf for computing alpha_i - scale_from : float - reference scale - scale_to : float - target scale + method : string + method for solving the RGE + rtol : float + relative acuracy of the solution Returns ------- @@ -724,6 +734,8 @@ def a( final scale to evolve to :math:`\mu_R^2` fact_scale : float factorization scale (if different from final scale) + nf_to : int + final nf value Returns ------- @@ -777,6 +789,8 @@ def a_s(self, scale_to, fact_scale=None, nf_to=None): final scale to evolve to :math:`\mu_R^2` fact_scale : float factorization scale (if different from final scale) + nf_to : int + final nf value Returns ------- @@ -796,6 +810,8 @@ def a_em(self, scale_to, fact_scale=None, nf_to=None): final scale to evolve to :math:`\mu_R^2` fact_scale : float factorization scale (if different from final scale) + nf_to : int + final nf value Returns ------- diff --git a/src/eko/kernels/valence_qed.py b/src/eko/kernels/valence_qed.py index 5a11fd98c..1775e7b66 100644 --- a/src/eko/kernels/valence_qed.py +++ b/src/eko/kernels/valence_qed.py @@ -1,8 +1,5 @@ """Collection of QED valence EKOs.""" import numba as nb -import numpy as np - -from ekore import anomalous_dimensions as ad from .singlet_qed import eko_iterate From e998a08f233697b2d0a4db966f5ec542176a8d38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 24 Feb 2023 12:54:16 +0100 Subject: [PATCH 300/312] Update docstrings in evolution_operator/__init__ --- src/eko/evolution_operator/__init__.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index c90581809..4f663045f 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -233,10 +233,14 @@ def quad_ker( target coupling value as0 : float initial coupling value - as_raw : float - coupling value at the process scale - aem : float - electromagnetic coupling value + mu2_from : float + initial value of mu2 + mu2_from : float + final value of mu2 + aem_list : list + list of electromagnetic coupling values + alphaem_running : bool + whether alphaem is running or not nf : int number of active flavors L : float @@ -342,8 +346,6 @@ def quad_ker_qcd( target coupling value as0 : float initial coupling value - as_raw : float - coupling value at the process scale nf : int number of active flavors L : float @@ -460,10 +462,14 @@ def quad_ker_qed( target coupling value as0 : float initial coupling value - as_raw : float - coupling value at the process scale - aem : float - electromagnetic coupling value + mu2_from : float + initial value of mu2 + mu2_from : float + final value of mu2 + aem_list : list + list of electromagnetic coupling values + alphaem_running : bool + whether alphaem is running or not nf : int number of active flavors L : float From b41102d1399cb333fb7102790abd1d8f25462fc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Mon, 27 Feb 2023 10:19:00 +0100 Subject: [PATCH 301/312] Remove useless import --- src/eko/evolution_operator/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 4f663045f..3a5a57ff0 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -24,7 +24,6 @@ from ..kernels import non_singlet_qed as qed_ns from ..kernels import singlet as s from ..kernels import singlet_qed as qed_s -from ..kernels import utils from ..kernels import valence_qed as qed_v from ..member import OpMember From 8de99a25fffd743ca57683b94af7bd60a786e1bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Tue, 28 Feb 2023 14:22:05 +0100 Subject: [PATCH 302/312] Update documentation --- doc/source/theory/DGLAP.rst | 33 +++++++++++++++++++++++++++++++++ doc/source/theory/pQCD.rst | 3 ++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/doc/source/theory/DGLAP.rst b/doc/source/theory/DGLAP.rst index 112fd8f73..91f7f9e11 100644 --- a/doc/source/theory/DGLAP.rst +++ b/doc/source/theory/DGLAP.rst @@ -522,3 +522,36 @@ evolution is simply an identity operation: e.g. for an intrinsic charm distribut After :doc:`crossing the mass threshold ` (charm in this example) the |PDF| can not be considered intrinsic any longer and hence, they have to be rejoined with their evolution basis elements and take then again part in the ordinary collinear evolution. + +Mixed |QCD| :math:`\otimes` |QED| evolution +----------------------------------------- + +For the moment in this case only the `exact` evolution is implemented. + +Singlet +^^^^^^^ + +The evolution is obtained in the same way of the pure |QCD| case, with the only difference that +now both :math:`\gamma` and :math:`\beta_{qcd}` contain the |QED| corrections. + +In the case in which :math:`\alpha_{em}` is running, at every step of the iteration the corresponding value +of :math:`a_{em}(a_s)` is used. + +Non singlet +^^^^^^^^^^^ + +For the non singlet, being it diagonal, the solution is straightforward. +When :math:`\alpha_{em}` is fixed, the terms proportional to it are just a constant in the splitting functions, and therefore +they can be integrated directly. For example at ``order=(1,1)`` we have + +.. math:: + \tilde E^{(1,1)}_{ns}(a_s \leftarrow a_s^0) &= \exp \Bigl( -\int_{\log \mu_0^2}^{\log \mu^2}d\log\mu^2 \gamma_{ns}^{(1,0)} a_s(\log\mu^2) + \gamma_{ns}^{(1,1)} a_s(\log\mu^2) a_{em} + \gamma_{ns}^{(0,1)} a_em \Bigr) \\ + & = \exp \Bigl( \int_{a_s^0}^{a_s}da_s\frac{\gamma_{ns}^{(1,0)} a_s + \gamma_{ns}^{(1,1)} a_s a_{em} + \gamma_{ns}^{(0,1)} a_em}{a_s^2(\beta_0 + \beta_0^{mix} a_{em})} -\int_{\log \mu_0^2}^{\log \mu^2}d\log\mu^2 \gamma_{ns}^{(0,1)} a_em\Bigr) + +In the last expression, the first term can be integrated with the :math:`j^{(n,m)` functions, while the second term is trivial. + +In the case of :math:`\alpha_{em}` running, the :math:`a_s` integration integral is divided in steps, such that in every step +:math:`\alpha_{em}` is considered constant. In this way the solution will be the product of the solutions of every integration step: + +.. math:: + \tilde E^{(1,1)}_{ns}(a_s \leftarrow a_s^0) = \prod\limits_{k=n}^{0} E^{(1,1)}_{ns}(a_s^{k+1} \leftarrow a_s^k, a_{em}^k) diff --git a/doc/source/theory/pQCD.rst b/doc/source/theory/pQCD.rst index 47f7a625f..fc0100803 100644 --- a/doc/source/theory/pQCD.rst +++ b/doc/source/theory/pQCD.rst @@ -129,10 +129,11 @@ Order specification In the code ``order=tuple(int,int)`` specifies the |QCD| and |QED| perturbative orders of the splitting functions in terms of :math:`a_s = \alpha_s/(4\pi)` and :math:`a_{em} = \alpha_{em}/(4\pi)`. The available perturbative expansions are the following: -- ``order=(0,0)``: it is the non evolution case in which :math:`a_s` and :math:`a_{em}` are kept fixed and the splitting functions are null. - ``order=(n,0)``: with :math:`n=1,2,3,4` correspond to the pure |QCD| evolution at |LO|, |NLO|, |NNLO| and |N3LO| in which the |QCD| splitting functions are expanded up to :math:`\mathcal{O}(a_s^n)` and the strong coupling is evolved using the n-th coefficient of the beta function, i.e. :math:`\beta_{n-1}`. - ``order=(n,m)``; with :math:`n=1,2,3,4` and :math:`m=1,2` corresponds to the mixed |QED| :math:`\otimes` |QCD| evolution in which the splitting functions are expanded up to :math:`\mathcal{O}(a_s^na_{em}^m)`, the stromg coupling is evolved using up to the n-th coefficient of the beta function and the electromagnetic coupling is kept fixed. +Observe that the case :math:`n=0` is not allowed, since it would correspond to the pure |QED| evolution or (if :math:`m > 0`) no evolution at all. + Sum Rules --------- From 9599f072164e92f05e54ef96b862e3375ad1e165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 2 Mar 2023 10:12:55 +0100 Subject: [PATCH 303/312] move evolution_integral-qed into extras --- .../evolution_integrals.py | 89 ++++++++++++++ src/eko/kernels/evolution_integrals.py | 111 ++---------------- tests/eko/kernels/test_ei.py | 3 - 3 files changed, 99 insertions(+), 104 deletions(-) create mode 100644 extras/evolution_integrals_qed/evolution_integrals.py diff --git a/extras/evolution_integrals_qed/evolution_integrals.py b/extras/evolution_integrals_qed/evolution_integrals.py new file mode 100644 index 000000000..f7892d550 --- /dev/null +++ b/extras/evolution_integrals_qed/evolution_integrals.py @@ -0,0 +1,89 @@ +@nb.njit(cache=True) +def j02(a1, a0, beta0): + r""":math:`j^{(0,2)}` exact evolution integral. + + .. math:: + j^{(0,2)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{\beta_0 a_s'^2} + = \frac{1.0 / a0 - 1.0 / as}{\beta_0} + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + beta0 : float + LO beta function coefficient + + Returns + ------- + j02 : float + integral + """ + return (1.0 / a0 - 1.0 / a1) / beta0 + + +@nb.njit(cache=True) +def j03_exact(a1, a0, beta0, b_vec): + r""":math:`j^{(0,3)}` exact evolution integral. + + .. math:: + j^{(0,3)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{\beta_0 a_s'^2 + \beta_1 a_s'^3} + = \frac{1.0 / a0 - 1.0 / as}{\beta_0 + \frac{b_1}{\beta_0} \left(\log(1 + 1 / (as b_1)) - \log(1 + 1 / (a0 b_1)\right) + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + beta0 : float + LO beta function coefficient + beta1 : float + NLO beta function coefficient + + Returns + ------- + j03_exact : float + integral + """ + b1 = b_vec[1] + return -(1.0 / a1 - 1.0 / a0) / beta0 + b1 / beta0 * ( + np.log(1.0 + 1.0 / (a1 * b1)) - np.log(1.0 + 1.0 / (a0 * b1)) + ) + + +@nb.njit(cache=True) +def j04_exact(a1, a0, beta0, b_vec): + r""":math:`j^{(0,4)}` exact evolution integral. + + .. math:: + j^{(0,4)}(a_s,a_s^0) &= \int\limits_{a_s^0}^{a_s}\!da_s'\, + \frac{1}{\beta_0 a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ + &= j^{(-1,0)}(a_s,a_s^0) - b_1 j^{(1,4)}(a_s,a_s^0) - b_2 j^{(2,4)}(a_s,a_s^0) + + Parameters + ---------- + a1 : float + target coupling value + a0 : float + initial coupling value + beta0 : float + LO beta function coefficient + beta1 : float + NLO beta function coefficient + beta2 : float + NNLO beta function coefficient + + Returns + ------- + j04_exact : complex + integral + """ + b1 = b_vec[1] + b2 = b_vec[2] + return ( + j02(a1, a0, beta0) + - b1 * j14_exact(a1, a0, beta0, b_vec) + - b2 * j24_exact(a1, a0, beta0, b_vec) + ) diff --git a/src/eko/kernels/evolution_integrals.py b/src/eko/kernels/evolution_integrals.py index 491fb2235..144a54d64 100644 --- a/src/eko/kernels/evolution_integrals.py +++ b/src/eko/kernels/evolution_integrals.py @@ -13,31 +13,6 @@ import numpy as np -@nb.njit(cache=True) -def j02(a1, a0, beta0): - r""":math:`j^{(0,2)}` exact evolution integral. - - .. math:: - j^{(0,2)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{\beta_0 a_s'^2} - = \frac{1.0 / a0 - 1.0 / as}{\beta_0} - - Parameters - ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - beta0 : float - LO beta function coefficient - - Returns - ------- - j02 : float - integral - """ - return (1.0 / a0 - 1.0 / a1) / beta0 - - @nb.njit(cache=True) def j12(a1, a0, beta0): r""" @@ -87,7 +62,7 @@ def j23_exact(a1, a0, beta0, b_vec): Returns ------- - j11 : float + j23_exact : float integral """ b1 = b_vec[1] @@ -113,7 +88,7 @@ def j23_expanded(a1, a0, beta0): Returns ------- - j11_exp : float + j23_expanded : float integral """ return 1.0 / beta0 * (a1 - a0) @@ -142,7 +117,7 @@ def j13_exact(a1, a0, beta0, b_vec): Returns ------- - j01 : float + j13_exact : float integral """ b1 = b_vec[1] @@ -170,43 +145,13 @@ def j13_expanded(a1, a0, beta0, b_vec): Returns ------- - j01_exp : float + j13_expanded : float integral """ b1 = b_vec[1] return j12(a1, a0, beta0) - b1 * j23_expanded(a1, a0, beta0) -@nb.njit(cache=True) -def j03_exact(a1, a0, beta0, b_vec): - r""":math:`j^{(0,3)}` exact evolution integral. - - .. math:: - j^{(0,3)}(a_s,a_s^0) = \int\limits_{a_s^0}^{a_s} \frac{da_s'}{\beta_0 a_s'^2 + \beta_1 a_s'^3} - = \frac{1.0 / a0 - 1.0 / as}{\beta_0 + \frac{b_1}{\beta_0} \left(\log(1 + 1 / (as b_1)) - \log(1 + 1 / (a0 b_1)\right) - - Parameters - ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - beta0 : float - LO beta function coefficient - beta1 : float - NLO beta function coefficient - - Returns - ------- - jm11 : float - integral - """ - b1 = b_vec[1] - return -(1.0 / a1 - 1.0 / a0) / beta0 + b1 / beta0 * ( - np.log(1.0 + 1.0 / (a1 * b1)) - np.log(1.0 + 1.0 / (a0 * b1)) - ) - - @nb.njit(cache=True) def j34_exact(a1, a0, beta0, b_vec): r""" @@ -238,7 +183,7 @@ def j34_exact(a1, a0, beta0, b_vec): Returns ------- - j22 : complex + j34_exact : complex integral """ b1 = b_vec[1] @@ -279,7 +224,7 @@ def j24_exact(a1, a0, beta0, b_vec): Returns ------- - j12 : complex + j24_exact : complex integral """ # pylint: disable=line-too-long b1 = b_vec[1] @@ -317,7 +262,7 @@ def j14_exact(a1, a0, beta0, b_vec): Returns ------- - j02 : complex + j14_exact : complex integral """ b1 = b_vec[1] @@ -348,7 +293,7 @@ def j34_expanded(a1, a0, beta0): Returns ------- - j22_exp : float + j34_expanded : float integral """ return 1 / (2 * beta0) * (a1**2 - a0**2) @@ -376,7 +321,7 @@ def j24_expanded(a1, a0, beta0, b_vec): Returns ------- - j12_exp : float + j24_expanded : float integral """ b1 = b_vec[1] @@ -407,7 +352,7 @@ def j14_expanded(a1, a0, beta0, b_vec): Returns ------- - j02_exp : float + j14_expanded : float integral """ b1 = b_vec[1] @@ -417,39 +362,3 @@ def j14_expanded(a1, a0, beta0, b_vec): - b1 * j24_expanded(a1, a0, beta0, b_vec) - b2 * j34_expanded(a1, a0, beta0) ) - - -@nb.njit(cache=True) -def j04_exact(a1, a0, beta0, b_vec): - r""":math:`j^{(0,4)}` exact evolution integral. - - .. math:: - j^{(0,4)}(a_s,a_s^0) &= \int\limits_{a_s^0}^{a_s}\!da_s'\, - \frac{1}{\beta_0 a_s'^2 + \beta_1 a_s'^3 + \beta_2 a_s'^4}\\ - &= j^{(-1,0)}(a_s,a_s^0) - b_1 j^{(1,4)}(a_s,a_s^0) - b_2 j^{(2,4)}(a_s,a_s^0) - - Parameters - ---------- - a1 : float - target coupling value - a0 : float - initial coupling value - beta0 : float - LO beta function coefficient - beta1 : float - NLO beta function coefficient - beta2 : float - NNLO beta function coefficient - - Returns - ------- - jm12 : complex - integral - """ - b1 = b_vec[1] - b2 = b_vec[2] - return ( - j02(a1, a0, beta0) - - b1 * j14_exact(a1, a0, beta0, b_vec) - - b2 * j24_exact(a1, a0, beta0, b_vec) - ) diff --git a/tests/eko/kernels/test_ei.py b/tests/eko/kernels/test_ei.py index ed7fc27c9..5c89bc6e0 100644 --- a/tests/eko/kernels/test_ei.py +++ b/tests/eko/kernels/test_ei.py @@ -37,16 +37,13 @@ def test_zero_qed(): for fnc in [ ei.j23_exact, ei.j13_exact, - ei.j03_exact, ei.j34_exact, ei.j24_exact, ei.j14_exact, - ei.j04_exact, ]: np.testing.assert_allclose(fnc(1, 1, beta0, b_vec), 0) for fnc in [ ei.j12, - ei.j02, ei.j23_expanded, ei.j34_expanded, ]: From ba63d108c6021faa5f7277b5d2393ef792b8b4d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 2 Mar 2023 15:00:59 +0100 Subject: [PATCH 304/312] Fix bug in alphaem running --- src/eko/evolution_operator/__init__.py | 116 ++++++++------- src/eko/kernels/non_singlet_qed.py | 35 +++-- src/eko/kernels/singlet_qed.py | 21 ++- src/eko/kernels/valence_qed.py | 7 +- tests/eko/evolution_operator/test_init.py | 30 ++-- tests/eko/kernels/test_kernels_QEDns.py | 53 ++++--- tests/eko/kernels/test_kernels_QEDsinglet.py | 22 ++- tests/eko/kernels/test_kernels_QEDvalence.py | 22 ++- tests/eko/test_couplings.py | 145 ------------------- 9 files changed, 150 insertions(+), 301 deletions(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 3a5a57ff0..5e32e411d 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -24,6 +24,7 @@ from ..kernels import non_singlet_qed as qed_ns from ..kernels import singlet as s from ..kernels import singlet_qed as qed_s +from ..kernels import utils from ..kernels import valence_qed as qed_v from ..member import OpMember @@ -193,11 +194,10 @@ def quad_ker( is_log, logx, areas, - as1, - as0, + as_list, mu2_from, mu2_to, - aem_list, + a_half, alphaem_running, nf, L, @@ -273,8 +273,8 @@ def quad_ker( mode0, mode1, method, - as1, - as0, + as_list[-1], + as_list[0], nf, L, ev_op_iterations, @@ -291,11 +291,10 @@ def quad_ker( mode0, mode1, method, - as1, - as0, + as_list, mu2_from, mu2_to, - aem_list, + a_half, alphaem_running, nf, L, @@ -430,11 +429,10 @@ def quad_ker_qed( mode0, mode1, method, - as1, - as0, + as_list, mu2_from, mu2_to, - aem_list, + a_half, alphaem_running, nf, L, @@ -499,18 +497,20 @@ def quad_ker_qed( order, method, gamma_s, - as1, - as0, - aem_list, + as_list, + a_half, nf, ev_op_iterations, ev_op_max_order, ) # scale var expanded is applied on the kernel + # TODO : in this way a_half[-1][1] is the aem value computed in + # the middle point of the last step. Instead we want aem computed in mu2_final. + # However the distance between the two is very small and affects only the running aem if sv_mode == sv.Modes.expanded and not is_threshold: ker = np.ascontiguousarray( sv.expanded.singlet_variation_qed( - gamma_s, as1, aem_list[-1], alphaem_running, order, nf, L + gamma_s, as_list[-1], a_half[-1][1], alphaem_running, order, nf, L ) ) @ np.ascontiguousarray(ker) ker = select_QEDsinglet_element(ker, mode0, mode1) @@ -525,9 +525,8 @@ def quad_ker_qed( order, method, gamma_v, - as1, - as0, - aem_list, + as_list, + a_half, nf, ev_op_iterations, ev_op_max_order, @@ -536,7 +535,7 @@ def quad_ker_qed( if sv_mode == sv.Modes.expanded and not is_threshold: ker = np.ascontiguousarray( sv.expanded.valence_variation_qed( - gamma_v, as1, aem_list[-1], alphaem_running, order, nf, L + gamma_v, as_list[-1], a_half[-1][1], alphaem_running, order, nf, L ) ) @ np.ascontiguousarray(ker) ker = select_QEDvalence_element(ker, mode0, mode1) @@ -551,9 +550,8 @@ def quad_ker_qed( order, method, gamma_ns, - as1, - as0, - aem_list, + as_list, + a_half[:, 1], alphaem_running, nf, ev_op_iterations, @@ -563,7 +561,7 @@ def quad_ker_qed( if sv_mode == sv.Modes.expanded and not is_threshold: ker = ( sv.expanded.non_singlet_variation_qed( - gamma_ns, as1, aem_list[-1], alphaem_running, order, nf, L + gamma_ns, as_list[-1], a_half[-1][1], alphaem_running, order, nf, L ) * ker ) @@ -614,7 +612,7 @@ def __init__( self.alphaem_running = self.managers["couplings"].alphaem_running if self.log_label == "Evolution": self.a = self.compute_a() - self.aem_list = self.compute_aem_list() + self.compute_aem_list() @property def n_pools(self): @@ -685,36 +683,45 @@ def a_em(self): def compute_aem_list(self): """Return the list of the couplings for the different values of :math:`a_s`.""" if self.order[1] == 0: - return np.array([]) - as0 = self.a_s[0] - as1 = self.a_s[1] - aem0 = self.a_em[0] - aem1 = self.a_em[1] - q2ref = self.managers["couplings"].q2_ref - delta_from = abs(self.q2_from - q2ref) - delta_to = abs(self.q2_to - q2ref) - # I compute the values in aem_list starting from the mu2 - # that is closer to mu_ref. - if delta_from > delta_to: - a_start = np.array([as1, aem1]) - mu2_start = self.q2_to + self.as_list = np.array([self.a_s[0], self.a_s[1]]) + self.a_half_list = np.array([]) else: - a_start = np.array([as0, aem0]) - mu2_start = self.q2_from - couplings = self.managers["couplings"] - ev_op_iterations = self.config["ev_op_iterations"] - mu2_steps = np.linspace(self.q2_from, self.q2_to, 1 + ev_op_iterations) - mu2_l = mu2_steps[0] - aem_list = [] - for mu2_h in mu2_steps[1:]: - mu2_half = (mu2_h + mu2_l) / 2.0 - aem_list.append( - couplings.compute( - a_ref=a_start, nf=self.nf, scale_from=mu2_start, scale_to=mu2_half - )[1] + as0 = self.a_s[0] + as1 = self.a_s[1] + aem0 = self.a_em[0] + aem1 = self.a_em[1] + q2ref = self.managers["couplings"].q2_ref + delta_from = abs(self.q2_from - q2ref) + delta_to = abs(self.q2_to - q2ref) + # I compute the values in aem_list starting from the mu2 + # that is closer to mu_ref. + if delta_from > delta_to: + a_start = np.array([as1, aem1]) + mu2_start = self.q2_to + else: + a_start = np.array([as0, aem0]) + mu2_start = self.q2_from + couplings = self.managers["couplings"] + ev_op_iterations = self.config["ev_op_iterations"] + mu2_steps = utils.geomspace(self.q2_from, self.q2_to, 1 + ev_op_iterations) + mu2_l = mu2_steps[0] + self.as_list = np.array( + [ + couplings.compute( + a_ref=a_start, nf=self.nf, scale_from=mu2_start, scale_to=mu2 + )[0] + for mu2 in mu2_steps + ] ) - mu2_l = mu2_h - return np.array(aem_list) + a_half = np.zeros((ev_op_iterations, 2)) + for step, mu2_h in enumerate(mu2_steps[1:]): + mu2_half = (mu2_h + mu2_l) / 2.0 + a_s, aem = couplings.compute( + a_ref=a_start, nf=self.nf, scale_from=mu2_start, scale_to=mu2_half + ) + a_half[step] = [a_s, aem] + mu2_l = mu2_h + self.a_half_list = a_half @property def labels(self): @@ -787,11 +794,10 @@ def quad_ker(self, label, logx, areas): is_log=self.int_disp.log, logx=logx, areas=areas, - as1=self.a_s[1], - as0=self.a_s[0], + as_list=self.as_list, mu2_from=self.q2_from, mu2_to=self.q2_to, - aem_list=self.aem_list, + a_half=self.a_half_list, alphaem_running=self.alphaem_running, nf=self.nf, L=np.log(self.xif2), diff --git a/src/eko/kernels/non_singlet_qed.py b/src/eko/kernels/non_singlet_qed.py index b92365c90..775c90534 100644 --- a/src/eko/kernels/non_singlet_qed.py +++ b/src/eko/kernels/non_singlet_qed.py @@ -125,9 +125,8 @@ def dispatcher( order, method, gamma_ns, - a1, - a0, - aem_list, + as_list, + aem_half, alphaem_running, nf, ev_op_iterations, @@ -168,13 +167,16 @@ def dispatcher( e_ns : complex non-singlet EKO """ - if not alphaem_running: - aem = aem_list[0] - return fixed_alphaem_exact(order, gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from) - else: - return running_alphaem_exact( - order, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations, mu2_to, mu2_from - ) + return exact( + order, + gamma_ns, + as_list, + aem_half, + nf, + 1 if not alphaem_running else ev_op_iterations, + mu2_to, + mu2_from, + ) @nb.njit(cache=True) @@ -220,9 +222,7 @@ def fixed_alphaem_exact(order, gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from): @nb.njit(cache=True) -def running_alphaem_exact( - order, gamma_ns, a1, a0, aem_list, nf, ev_op_iterations, mu2_to, mu2_from -): +def exact(order, gamma_ns, as_list, aem_half, nf, ev_op_iterations, mu2_to, mu2_from): """Compute exact solution for running alphaem. Parameters @@ -251,13 +251,12 @@ def running_alphaem_exact( e_ns : complex non-singlet EKO """ - a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) - mu2_steps = np.linspace(mu2_from, mu2_to, 1 + ev_op_iterations) + mu2_steps = utils.geomspace(mu2_from, mu2_to, 1 + ev_op_iterations) res = 1.0 for step in range(1, ev_op_iterations + 1): - aem = aem_list[step - 1] - a1 = a_steps[step] - a0 = a_steps[step - 1] + aem = aem_half[step - 1] + a1 = as_list[step] + a0 = as_list[step - 1] mu2_from = mu2_steps[step - 1] mu2_to = mu2_steps[step] res *= fixed_alphaem_exact(order, gamma_ns, a1, a0, aem, nf, mu2_to, mu2_from) diff --git a/src/eko/kernels/singlet_qed.py b/src/eko/kernels/singlet_qed.py index 52ce456e6..e85ad5112 100644 --- a/src/eko/kernels/singlet_qed.py +++ b/src/eko/kernels/singlet_qed.py @@ -9,7 +9,7 @@ @nb.njit(cache=True) -def eko_iterate(gamma_singlet, a1, a0, aem_list, nf, order, ev_op_iterations, dim): +def eko_iterate(gamma_singlet, as_list, a_half, nf, order, ev_op_iterations, dim): """Singlet QEDxQCD iterated (exact) EKO. Parameters @@ -34,23 +34,23 @@ def eko_iterate(gamma_singlet, a1, a0, aem_list, nf, order, ev_op_iterations, di e_s^{order} : numpy.ndarray singlet QEDxQCD iterated (exact) EKO """ - a_steps = utils.geomspace(a0, a1, 1 + ev_op_iterations) e = np.identity(dim, np.complex_) betaQCD = np.zeros((order[0] + 1, order[1] + 1)) for i in range(1, order[0] + 1): betaQCD[i, 0] = beta.beta_qcd((i + 1, 0), nf) betaQCD[1, 1] = beta.beta_qcd((2, 1), nf) for step in range(1, ev_op_iterations + 1): - ah = a_steps[step] - al = a_steps[step - 1] - a_half = (ah + al) / 2.0 + ah = as_list[step] + al = as_list[step - 1] + as_half = a_half[step - 1, 0] + aem = a_half[step - 1, 1] delta_a = ah - al gamma = np.zeros((dim, dim), np.complex_) betatot = 0 for i in range(0, order[0] + 1): for j in range(0, order[1] + 1): - betatot += betaQCD[i, j] * a_half ** (i + 1) * aem_list[step - 1] ** j - gamma += gamma_singlet[i, j] * a_half**i * aem_list[step - 1] ** j + betatot += betaQCD[i, j] * as_half ** (i + 1) * aem**j + gamma += gamma_singlet[i, j] * as_half**i * aem**j ln = gamma / betatot * delta_a ek = np.ascontiguousarray(ad.exp_matrix(ln)[0]) e = ek @ e @@ -62,9 +62,8 @@ def dispatcher( order, method, gamma_singlet, - a1, - a0, - aem_list, + as_list, + a_half, nf, ev_op_iterations, ev_op_max_order, @@ -99,6 +98,6 @@ def dispatcher( """ if method in ["iterate-exact", "iterate-expanded"]: return eko_iterate( - gamma_singlet, a1, a0, aem_list, nf, order, ev_op_iterations, 4 + gamma_singlet, as_list, a_half, nf, order, ev_op_iterations, 4 ) raise NotImplementedError('Only "iterate-exact" is implemented with QED') diff --git a/src/eko/kernels/valence_qed.py b/src/eko/kernels/valence_qed.py index 1775e7b66..3a8482f68 100644 --- a/src/eko/kernels/valence_qed.py +++ b/src/eko/kernels/valence_qed.py @@ -9,9 +9,8 @@ def dispatcher( order, method, gamma_valence, - a1, - a0, - aem_list, + as_list, + a_half, nf, ev_op_iterations, ev_op_max_order, @@ -47,6 +46,6 @@ def dispatcher( """ if method in ["iterate-exact", "iterate-expanded"]: return eko_iterate( - gamma_valence, a1, a0, aem_list, nf, order, ev_op_iterations, 2 + gamma_valence, as_list, a_half, nf, order, ev_op_iterations, 2 ) raise NotImplementedError('Only "iterate-exact" is implemented with QED') diff --git a/tests/eko/evolution_operator/test_init.py b/tests/eko/evolution_operator/test_init.py index b79965c46..6c5629649 100644 --- a/tests/eko/evolution_operator/test_init.py +++ b/tests/eko/evolution_operator/test_init.py @@ -29,11 +29,10 @@ def test_quad_ker_errors(): is_log=True, logx=0.1, areas=[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]], - as1=1, - as0=2, + as_list=[2.0, 1.0], mu2_from=1.0, mu2_to=2.0, - aem_list=[0.01], + a_half=np.array([[1.5, 0.01]]), alphaem_running=False, nf=3, L=0, @@ -80,11 +79,10 @@ def test_quad_ker(monkeypatch): is_log=is_log, logx=logx, areas=np.zeros(3), - as1=1, - as0=2, + as_list=[2.0, 1.0], mu2_from=1, mu2_to=2, - aem_list=[0.00058], + a_half=np.array([[1.5, 0.01]]), alphaem_running=False, nf=3, L=0, @@ -108,11 +106,10 @@ def test_quad_ker(monkeypatch): is_log=True, logx=0.123, areas=np.zeros(3), - as1=1, - as0=2, + as_list=[2.0, 1.0], mu2_from=1, mu2_to=2, - aem_list=[0.00058], + a_half=np.array([[1.5, 0.01]]), alphaem_running=False, nf=3, L=0, @@ -143,11 +140,10 @@ def test_quad_ker(monkeypatch): is_log=True, logx=0.123, areas=np.zeros(3), - as1=1, - as0=2, + as_list=[2.0, 1.0], mu2_from=1, mu2_to=2, - aem_list=[0.00058], + a_half=np.array([[1.5, 0.01]]), alphaem_running=False, nf=3, L=0, @@ -170,11 +166,10 @@ def test_quad_ker(monkeypatch): is_log=True, logx=0.0, areas=np.zeros(3), - as1=1, - as0=2, + as_list=[2.0, 1.0], mu2_from=1, mu2_to=2, - aem_list=[0.00058], + a_half=np.array([[1.5, 0.01]]), alphaem_running=False, nf=3, L=0, @@ -453,11 +448,10 @@ def quad_ker_pegasus( int_disp.log, logx, bf.areas_representation, - a1, - a0, + [a0, a1], mu2_from, mu2_to, - [0.00058], + [[(a1 + a0) / 2, 0.00058]], False, nf, L, diff --git a/tests/eko/kernels/test_kernels_QEDns.py b/tests/eko/kernels/test_kernels_QEDns.py index e38c009ab..bb6208363 100644 --- a/tests/eko/kernels/test_kernels_QEDns.py +++ b/tests/eko/kernels/test_kernels_QEDns.py @@ -38,8 +38,7 @@ def test_zero(): order, method, gamma_ns, - 1.0, - 1.0, + [1.0, 1.0, 1.0], [1.0, 1.0], running, nf, @@ -54,8 +53,7 @@ def test_zero(): order, method, np.zeros((qcd + 1, qed + 1), dtype=complex), - 2.0, - 1.0, + [1.0, 1.5, 2.0], [1.0, 1.0], running, nf, @@ -87,8 +85,7 @@ def test_zero_true_gamma(): order, method, gamma_ns, - 1.0, - 1.0, + [1.0, 1.0, 1.0], [1.0, 1.0], running, nf, @@ -103,8 +100,7 @@ def test_zero_true_gamma(): order, method, np.zeros((qcd + 1, qed + 1), dtype=complex), - 2.0, - 1.0, + [1.0, 1.5, 2.0], [1.0, 1.0], running, nf, @@ -142,7 +138,7 @@ def test_zero_true_gamma(): def test_ode(): - ev_op_iterations = 10 + ev_op_iterations = 1 aem_list = [0.00781] * ev_op_iterations nf = 5 delta_mu2 = 1e-6 @@ -186,8 +182,7 @@ def test_ode(): order, method, gamma_ns, - a1, - a0, + np.geomspace(a0, a1, ev_op_iterations + 1), aem_list, False, nf, @@ -200,8 +195,11 @@ def test_ode(): order, method, gamma_ns, - sc.a_s(mu2_to + 0.5 * delta_mu2), - a0, + np.geomspace( + a0, + sc.a_s(mu2_to + 0.5 * delta_mu2), + ev_op_iterations + 1, + ), aem_list, False, nf, @@ -213,8 +211,11 @@ def test_ode(): order, method, gamma_ns, - sc.a_s(mu2_to - 0.5 * delta_mu2), - a0, + np.geomspace( + a0, + sc.a_s(mu2_to - 0.5 * delta_mu2), + ev_op_iterations + 1, + ), aem_list, False, nf, @@ -227,7 +228,7 @@ def test_ode(): def test_ode_true_gamma(): - ev_op_iterations = 10 + ev_op_iterations = 1 aem_list = [0.00781] * ev_op_iterations nf = 5 delta_mu2 = 1e-6 @@ -264,8 +265,7 @@ def test_ode_true_gamma(): order, method, gamma_ns, - a1, - a0, + np.geomspace(a0, a1, ev_op_iterations + 1), aem_list, False, nf, @@ -278,8 +278,11 @@ def test_ode_true_gamma(): order, method, gamma_ns, - sc.a_s(mu2_to + 0.5 * delta_mu2), - a0, + np.geomspace( + a0, + sc.a_s(mu2_to + 0.5 * delta_mu2), + ev_op_iterations + 1, + ), aem_list, False, nf, @@ -291,8 +294,11 @@ def test_ode_true_gamma(): order, method, gamma_ns, - sc.a_s(mu2_to - 0.5 * delta_mu2), - a0, + np.geomspace( + a0, + sc.a_s(mu2_to - 0.5 * delta_mu2), + ev_op_iterations + 1, + ), aem_list, False, nf, @@ -311,8 +317,7 @@ def test_error(): (4, 2), "iterate-exact", np.random.rand(4, 3), - 0.2, - 0.1, + [0.1, 0.2], [0.01], running, 3, diff --git a/tests/eko/kernels/test_kernels_QEDsinglet.py b/tests/eko/kernels/test_kernels_QEDsinglet.py index 9fc3a43fb..f9729dfbe 100644 --- a/tests/eko/kernels/test_kernels_QEDsinglet.py +++ b/tests/eko/kernels/test_kernels_QEDsinglet.py @@ -49,9 +49,8 @@ def test_zero(monkeypatch): order, method, gamma_s, - 1, - 1, - [1, 1], + [1, 1, 1], + np.array([[1, 1], [1, 1]]), nf, ev_op_iterations, ev_op_max_order, @@ -63,9 +62,8 @@ def test_zero(monkeypatch): order, method, np.zeros((qcd + 1, qed + 1, 4, 4), dtype=complex), - 2, - 1, - [1, 1], + [1, 1.5, 2], + np.array([[1, 1], [1, 1]]), nf, ev_op_iterations, ev_op_max_order, @@ -103,9 +101,8 @@ def test_zero_true_gamma(monkeypatch): order, method, gamma_s, - 1, - 1, - [1, 1], + [1, 1, 1], + np.array([[1, 1], [1, 1]]), nf, ev_op_iterations, ev_op_max_order, @@ -117,9 +114,8 @@ def test_zero_true_gamma(monkeypatch): order, method, np.zeros((qcd + 1, qed + 1, 4, 4), dtype=complex), - 2, - 1, - [1, 1], + [1.0, 1.5, 2.0], + np.array([[1.25, 1], [1.75, 1]]), nf, ev_op_iterations, ev_op_max_order, @@ -131,5 +127,5 @@ def test_zero_true_gamma(monkeypatch): def test_error(): with pytest.raises(NotImplementedError): s.dispatcher( - (3, 2), "AAA", np.random.rand(4, 3, 2, 2), 0.2, 0.1, 0.01, 3, 10, 10 + (3, 2), "AAA", np.random.rand(4, 3, 2, 2), [0.2, 0.1], [0.01], 3, 10, 10 ) diff --git a/tests/eko/kernels/test_kernels_QEDvalence.py b/tests/eko/kernels/test_kernels_QEDvalence.py index 5c9780b04..35320c90d 100644 --- a/tests/eko/kernels/test_kernels_QEDvalence.py +++ b/tests/eko/kernels/test_kernels_QEDvalence.py @@ -47,9 +47,8 @@ def test_zero(monkeypatch): order, method, gamma_v, - 1, - 1, - [1, 1], + [1, 1, 1], + np.array([[1, 1], [1, 1]]), nf, ev_op_iterations, ev_op_max_order, @@ -61,9 +60,8 @@ def test_zero(monkeypatch): order, method, np.zeros((qcd + 1, qed + 1, 2, 2), dtype=complex), - 2, - 1, - [1, 1], + [1.0, 1.5, 2.0], + np.array([[1.25, 1], [1.75, 1]]), nf, ev_op_iterations, ev_op_max_order, @@ -101,9 +99,8 @@ def test_zero_true_gamma(monkeypatch): order, method, gamma_v, - 1, - 1, - [1, 1], + [1, 1, 1], + np.array([[1, 1], [1, 1]]), nf, ev_op_iterations, ev_op_max_order, @@ -115,9 +112,8 @@ def test_zero_true_gamma(monkeypatch): order, method, np.zeros((qcd + 1, qed + 1, 2, 2), dtype=complex), - 2, - 1, - [1, 1], + [1.0, 1.5, 2.0], + np.array([[1.25, 1], [1.75, 1]]), nf, ev_op_iterations, ev_op_max_order, @@ -129,5 +125,5 @@ def test_zero_true_gamma(monkeypatch): def test_error(): with pytest.raises(NotImplementedError): val.dispatcher( - (3, 2), "AAA", np.random.rand(4, 3, 2, 2), 0.2, 0.1, 0.01, 3, 10, 10 + (3, 2), "AAA", np.random.rand(4, 3, 2, 2), [0.2, 0.1], [0.01], 3, 10, 10 ) diff --git a/tests/eko/test_couplings.py b/tests/eko/test_couplings.py index f517e8249..01ece7682 100644 --- a/tests/eko/test_couplings.py +++ b/tests/eko/test_couplings.py @@ -324,148 +324,3 @@ def benchmark_expanded_n3lo(self): np.testing.assert_allclose( mathematica_val, as_N3LO.a(Q2)[0] - as_NNLO.a(Q2)[0], rtol=5e-7 ) - - # def test_compute_aem_as(self): - # alphas_ref = 0.118 - # alphaem_ref = 0.00781 - # scale_ref = 91.2**2 - # thresh_setup = (2, 4, 175) - # scale_target = [5, 10, 50, 100, 150] # nf = 5 - # for alphaem_running in [False]: - # for qcd in range(1, 4 + 1): - # for qed in range(0, 2 + 1): - # couplings = Couplings( - # np.array([alphas_ref, alphaem_ref]), - # scale_ref, - # thresh_setup, - # (1.0, 1.0, 1.0), - # (qcd, qed), - # "exact", - # nf_ref=5, - # alphaem_running=alphaem_running, - # ) - # a_values = [] - # for Qf in scale_target: - # a_values.append(couplings.a(Qf**2)) - # for a in a_values: - # aem = couplings.compute_aem_as( - # alphaem_ref / 4 / np.pi, alphas_ref / 4 / np.pi, a[0], nf=5 - # ) - # np.testing.assert_allclose(aem, a[1], atol=1e-10, rtol=1e-10) - # for alphaem_running in [True, False]: - # for qcd in range(1, 4 + 1): - # for qed in range(0, 0 + 1): - # couplings = Couplings( - # np.array([alphas_ref, alphaem_ref]), - # scale_ref, - # thresh_setup, - # (1.0, 1.0, 1.0), - # (qcd, qed), - # "exact", - # nf_ref=5, - # alphaem_running=alphaem_running, - # ) - # a_values = [] - # for Qf in scale_target: - # a_values.append(couplings.a(Qf**2)) - # for a in a_values: - # aem = couplings.compute_aem_as( - # alphaem_ref / 4 / np.pi, alphas_ref / 4 / np.pi, a[0], nf=5 - # ) - # np.testing.assert_allclose(aem, a[1], atol=1e-10, rtol=1e-10) - # for alphaem_running in [True]: - # for qcd in range(1, 4 + 1): - # for qed in range(1, 2 + 1): - # couplings = Couplings( - # np.array([alphas_ref, alphaem_ref]), - # scale_ref, - # thresh_setup, - # (1.0, 1.0, 1.0), - # (qcd, qed), - # "exact", - # nf_ref=5, - # alphaem_running=alphaem_running, - # ) - # a_values = [] - # for Qf in scale_target: - # a_values.append(couplings.a(Qf**2)) - # for a in a_values: - # aem = couplings.compute_aem_as( - # alphaem_ref / 4 / np.pi, alphas_ref / 4 / np.pi, a[0], nf=5 - # ) - # np.testing.assert_allclose(aem, a[1], atol=1e-6, rtol=1e-2) - # scale_target = [2.1, 2.5, 3.0, 3.5] # nf = 4 - # for alphaem_running in [True]: - # for qcd in range(1, 4 + 1): - # for qed in range(1, 2 + 1): - # couplings = Couplings( - # np.array([alphas_ref, alphaem_ref]), - # scale_ref, - # thresh_setup, - # (1.0, 1.0, 1.0), - # (qcd, qed), - # "exact", - # nf_ref=5, - # alphaem_running=alphaem_running, - # ) - # a_ref = couplings.a(4.0**2, nf_to=4) - # a_values = [] - # for Qf in scale_target: - # a_values.append(couplings.a(Qf**2)) - # for a in a_values: - # aem = couplings.compute_aem_as(a_ref[1], a_ref[0], a[0], nf=4) - # np.testing.assert_allclose(aem, a[1], atol=1e-6, rtol=1e-4) - - # def test_as_aem(self): - # # prepare - # thresh_setups = [ - # (np.inf, np.inf, np.inf), - # (0, np.inf, np.inf), - # (2, 4, 175), - # ] - # alphas_ref = 0.118 - # alphaem_ref = 0.00781 - # scale_ref = 91.0**2 - # for thresh_setup in thresh_setups: - # for qcd in range(1, 4 + 1): - # for qed in range(2 + 1): - # for mode_ev in ["expanded", "exact"]: - # for q2 in [ - # 1.0**2, - # 3.0**2, - # 4.0**2, - # 10.0**2, - # 50**2, - # 100**2, - # 150.0**2, - # 180.0**2, - # ]: - # for fact_scale_ratio in [0.5**2, 1.0**2, 2.0**2]: - # for alphaem_running in [True, False]: - # coupling = Couplings( - # np.array([alphas_ref, alphaem_ref]), - # scale_ref, - # thresh_setup, - # (1.0, 1.0, 1.0), - # (qcd, qed), - # mode_ev, - # alphaem_running=alphaem_running, - # ) - # np.testing.assert_allclose( - # coupling.a( - # q2, fact_scale=q2 * fact_scale_ratio - # )[0], - # coupling.a_s( - # q2, fact_scale=q2 * fact_scale_ratio - # ), - # rtol=1e-10, - # ) - # np.testing.assert_allclose( - # coupling.a( - # q2, fact_scale=q2 * fact_scale_ratio - # )[1], - # coupling.a_em( - # q2, fact_scale=q2 * fact_scale_ratio - # ), - # rtol=1e-10, - # ) From 314d769c06f8beecfab700719b4d099ca393657a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 2 Mar 2023 16:05:21 +0100 Subject: [PATCH 305/312] Put a_half to zero for QCD evol --- src/eko/evolution_operator/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 5e32e411d..084e03774 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -684,7 +684,7 @@ def compute_aem_list(self): """Return the list of the couplings for the different values of :math:`a_s`.""" if self.order[1] == 0: self.as_list = np.array([self.a_s[0], self.a_s[1]]) - self.a_half_list = np.array([]) + self.a_half_list = np.zeros((ev_op_iterations, 2)) else: as0 = self.a_s[0] as1 = self.a_s[1] From 96ef6fddb47e376fb197d78d1bf88c1a074a9026 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 2 Mar 2023 16:08:10 +0100 Subject: [PATCH 306/312] Fix last commit --- src/eko/evolution_operator/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 084e03774..94f0ba003 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -682,6 +682,7 @@ def a_em(self): def compute_aem_list(self): """Return the list of the couplings for the different values of :math:`a_s`.""" + ev_op_iterations = self.config["ev_op_iterations"] if self.order[1] == 0: self.as_list = np.array([self.a_s[0], self.a_s[1]]) self.a_half_list = np.zeros((ev_op_iterations, 2)) @@ -702,7 +703,6 @@ def compute_aem_list(self): a_start = np.array([as0, aem0]) mu2_start = self.q2_from couplings = self.managers["couplings"] - ev_op_iterations = self.config["ev_op_iterations"] mu2_steps = utils.geomspace(self.q2_from, self.q2_to, 1 + ev_op_iterations) mu2_l = mu2_steps[0] self.as_list = np.array( From c62e6a2cfbd31c73f2d0bf457664bb484177f0e2 Mon Sep 17 00:00:00 2001 From: niclaurenti Date: Thu, 2 Mar 2023 16:25:21 +0100 Subject: [PATCH 307/312] Fix test --- tests/eko/evolution_operator/test_init.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/eko/evolution_operator/test_init.py b/tests/eko/evolution_operator/test_init.py index 6c5629649..054407c8a 100644 --- a/tests/eko/evolution_operator/test_init.py +++ b/tests/eko/evolution_operator/test_init.py @@ -207,6 +207,7 @@ def test_labels(self): debug_skip_singlet=False, n_integration_cores=1, ModSV=None, + ev_op_iterations=1 ), fake_managers, 3, @@ -221,6 +222,7 @@ def test_labels(self): debug_skip_singlet=True, n_integration_cores=1, ModSV=None, + ev_op_iterations=1 ), fake_managers, 3, @@ -273,6 +275,7 @@ def test_n_pools(self): debug_skip_singlet=True, n_integration_cores=-excluded_cores, ModSV=None, + ev_op_iterations=1 ), fake_managers, 3, From 166b036a4c9fdee87c91a2ef9f3bf85542a1a2fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Fri, 3 Mar 2023 17:31:17 +0100 Subject: [PATCH 308/312] Fix bug in kernels/non_sing_qed --- src/eko/kernels/non_singlet_qed.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eko/kernels/non_singlet_qed.py b/src/eko/kernels/non_singlet_qed.py index 775c90534..a770687e4 100644 --- a/src/eko/kernels/non_singlet_qed.py +++ b/src/eko/kernels/non_singlet_qed.py @@ -173,7 +173,7 @@ def dispatcher( as_list, aem_half, nf, - 1 if not alphaem_running else ev_op_iterations, + ev_op_iterations, mu2_to, mu2_from, ) From 01a103f3d0a4086e596f514e627643a07adc0078 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 9 Mar 2023 12:20:27 +0100 Subject: [PATCH 309/312] Remove call to exp_matrix_2D in exp_matrix --- src/ekore/anomalous_dimensions/__init__.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ekore/anomalous_dimensions/__init__.py b/src/ekore/anomalous_dimensions/__init__.py index 2e1c0389f..254070755 100644 --- a/src/ekore/anomalous_dimensions/__init__.py +++ b/src/ekore/anomalous_dimensions/__init__.py @@ -83,11 +83,11 @@ def exp_matrix(gamma): """ dim = gamma.shape[0] e = np.zeros((dim, dim, dim), np.complex_) - if dim == 2: - exp, lambda_p, lambda_m, e_p, e_m = exp_matrix_2D(gamma) - e[0] = e_p - e[1] = e_m - return exp, np.array([lambda_p, lambda_m]), e + # if dim == 2: + # exp, lambda_p, lambda_m, e_p, e_m = exp_matrix_2D(gamma) + # e[0] = e_p + # e[1] = e_m + # return exp, np.array([lambda_p, lambda_m]), e w, v = np.linalg.eig(gamma) v_inv = np.linalg.inv(v) exp = np.zeros((dim, dim), np.complex_) From 83e553bb1a47857fb7d957d86fd2dbd50c221605 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 9 Mar 2023 12:23:55 +0100 Subject: [PATCH 310/312] Add brief docstring to compute_aem_list --- src/eko/evolution_operator/__init__.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 94f0ba003..81f10ee61 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -681,7 +681,15 @@ def a_em(self): return (self.a[0][1], self.a[1][1]) def compute_aem_list(self): - """Return the list of the couplings for the different values of :math:`a_s`.""" + """ + Return the list of the couplings for the different values of :math:`a_s`. + + This functions is needed in order to compute the values of :math:`a_s` + and :math:`a_em` in the middle point of the :math:`mu^2` interval, and + the values of :math:`a_s` at the borders of every intervals. + This is needed in the running_alphaem solution. + + """ ev_op_iterations = self.config["ev_op_iterations"] if self.order[1] == 0: self.as_list = np.array([self.a_s[0], self.a_s[1]]) From e53f7b47dd08f2f98648f838d645c1a12fa273d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 9 Mar 2023 12:34:18 +0100 Subject: [PATCH 311/312] Fix broken test --- tests/eko/kernels/test_kernels_QEDvalence.py | 30 +++----------------- 1 file changed, 4 insertions(+), 26 deletions(-) diff --git a/tests/eko/kernels/test_kernels_QEDvalence.py b/tests/eko/kernels/test_kernels_QEDvalence.py index 35320c90d..8cb1ecf91 100644 --- a/tests/eko/kernels/test_kernels_QEDvalence.py +++ b/tests/eko/kernels/test_kernels_QEDvalence.py @@ -30,17 +30,6 @@ def test_zero(monkeypatch): np.random.rand(qcd + 1, qed + 1, 2, 2) + np.random.rand(qcd + 1, qed + 1, 2, 2) * 1j ) - monkeypatch.setattr( - anomalous_dimensions, - "exp_matrix_2D", - lambda gamma_v: ( - gamma_v, - 1, - 1, - np.array([[1, 0], [0, 0]]), - np.array([[0, 0], [0, 1]]), - ), - ) for method in methods: np.testing.assert_allclose( val.dispatcher( @@ -53,7 +42,7 @@ def test_zero(monkeypatch): ev_op_iterations, ev_op_max_order, ), - np.zeros((2, 2)), + np.identity(2), ) np.testing.assert_allclose( val.dispatcher( @@ -66,7 +55,7 @@ def test_zero(monkeypatch): ev_op_iterations, ev_op_max_order, ), - np.zeros((2, 2)), + np.identity(2), ) @@ -82,17 +71,6 @@ def test_zero_true_gamma(monkeypatch): gamma_v = anomalous_dimensions.unpolarized.space_like.gamma_valence_qed( order, n, nf ) - monkeypatch.setattr( - anomalous_dimensions, - "exp_matrix_2D", - lambda gamma_v: ( - gamma_v, - 1, - 1, - np.array([[1, 0], [0, 0]]), - np.array([[0, 0], [0, 1]]), - ), - ) for method in methods: np.testing.assert_allclose( val.dispatcher( @@ -105,7 +83,7 @@ def test_zero_true_gamma(monkeypatch): ev_op_iterations, ev_op_max_order, ), - np.zeros((2, 2)), + np.identity(2), ) np.testing.assert_allclose( val.dispatcher( @@ -118,7 +96,7 @@ def test_zero_true_gamma(monkeypatch): ev_op_iterations, ev_op_max_order, ), - np.zeros((2, 2)), + np.identity(2), ) From e18b6440c833e9fd97fec6bfa5be4a7d852f48b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Laurenti?= Date: Thu, 9 Mar 2023 14:28:23 +0100 Subject: [PATCH 312/312] Add QrefQED to benchmarks --- benchmarks/CT14_bench.py | 4 +++- benchmarks/CT18_bench.py | 2 +- benchmarks/HERA20_bench.py | 3 +++ benchmarks/NNPDF_bench.py | 8 ++++---- benchmarks/lha_paper_bench.py | 3 +++ benchmarks/pegasus_bench.py | 6 +++++- benchmarks/sandbox.py | 3 +++ 7 files changed, 22 insertions(+), 7 deletions(-) diff --git a/benchmarks/CT14_bench.py b/benchmarks/CT14_bench.py index ce6ca2cb1..31a1e87d3 100644 --- a/benchmarks/CT14_bench.py +++ b/benchmarks/CT14_bench.py @@ -5,9 +5,10 @@ whereas the lo family uses LO splitting functions with NLO alphas evolution """ +from math import nan + from banana import register -from eko import compatibility from ekomark.benchmark.runner import Runner register(__file__) @@ -15,6 +16,7 @@ base_theory = { "Qref": 91.1876, + "QrefQED": nan, "mc": 1.3, "mb": 4.75, "mt": 172, diff --git a/benchmarks/CT18_bench.py b/benchmarks/CT18_bench.py index 29f048337..cc7c06e47 100644 --- a/benchmarks/CT18_bench.py +++ b/benchmarks/CT18_bench.py @@ -104,4 +104,4 @@ def benchmark_znnlo(self, Q0=1.3, Q2grid=(1e4,)): if __name__ == "__main__": b = BenchmarkCT18() - b.benchmark_nnlo_qed() + b.benchmark_nnlo() diff --git a/benchmarks/HERA20_bench.py b/benchmarks/HERA20_bench.py index f7d77fce8..057a25954 100644 --- a/benchmarks/HERA20_bench.py +++ b/benchmarks/HERA20_bench.py @@ -2,6 +2,8 @@ Benchmark HERAPDF2.0 pdf family """ +from math import nan + from banana import register from eko import interpolation @@ -12,6 +14,7 @@ base_theory = { "Qref": 91.1876, + "QrefQED": nan, "mc": 1.43, "mb": 4.5, "mt": 173.0, diff --git a/benchmarks/NNPDF_bench.py b/benchmarks/NNPDF_bench.py index 5de95ceeb..008b0e4a2 100644 --- a/benchmarks/NNPDF_bench.py +++ b/benchmarks/NNPDF_bench.py @@ -155,10 +155,10 @@ def benchmark(self, Q0=1.65, Q2grid=(100,)): # nn31.benchmark_nlo(Q0=np.sqrt(low2), Q2grid=[10]) # # test backward # #nn31.benchmark_nlo(Q0=np.sqrt(high2), Q2grid=[low2]) - # nn40 = BenchmarkNNPDF40() - # # nn40.benchmark_nnlo(Q2grid=[100]) + nn40 = BenchmarkNNPDF40() + nn40.benchmark_nnlo(Q2grid=[100]) # nn40.benchmark_nnlo(Q0=np.sqrt(high2), Q2grid=[low2]) # nnpol = BenchmarkNNPDFpol11() # nnpol.benchmark(Q0=np.sqrt(low2), Q2grid=[high2]) - obj = BenchmarkNNPDF31_luxqed() - obj.benchmark_nnlo(Q0=5.0) + # obj = BenchmarkNNPDF31_luxqed() + # obj.benchmark_nnlo(Q0=5.0) diff --git a/benchmarks/lha_paper_bench.py b/benchmarks/lha_paper_bench.py index e78765a60..316acffdd 100644 --- a/benchmarks/lha_paper_bench.py +++ b/benchmarks/lha_paper_bench.py @@ -1,6 +1,8 @@ """ Benchmark to :cite:`Giele:2002hx` (LO + NLO) and :cite:`Dittmar:2005ed` (NNLO). """ +from math import nan + import numpy as np from banana import register from banana.data import cartesian_product @@ -27,6 +29,7 @@ "alphas": 0.35, # Eq. (4.55) :cite:`Dittmar:2005ed` "alphaqed": 0.007496, "QED": 0, + "QrefQED": nan, } """Global theory settings""" diff --git a/benchmarks/pegasus_bench.py b/benchmarks/pegasus_bench.py index e3152148a..f417c812d 100644 --- a/benchmarks/pegasus_bench.py +++ b/benchmarks/pegasus_bench.py @@ -1,6 +1,8 @@ """ Benchmark to Pegasus :cite:`Vogt:2004ns` """ +from math import nan + import numpy as np from banana import register from banana.data import cartesian_product @@ -54,6 +56,7 @@ class BenchmarkVFNS(PegasusBenchmark): "kbThr": 1.0, "ktThr": 1.0, "Qref": np.sqrt(2.0), + "QrefQED": nan, "alphas": 0.35, "alphaqed": 0.007496, "QED": 0, @@ -107,7 +110,9 @@ class BenchmarkFFNS(PegasusBenchmark): "kbThr": np.inf, "ktThr": np.inf, "Qref": np.sqrt(2.0), + "QrefQED": nan, "alphas": 0.35, + "alphaqed": 0.007496, "Q0": np.sqrt(2.0), } ffns_theory = tolist(ffns_theory) @@ -151,7 +156,6 @@ def benchmark_sv(self, pto, svmode): if __name__ == "__main__": - # obj = BenchmarkVFNS() obj = BenchmarkFFNS() obj.benchmark_plain_pol(1) diff --git a/benchmarks/sandbox.py b/benchmarks/sandbox.py index 721bc30e9..3d332b52f 100644 --- a/benchmarks/sandbox.py +++ b/benchmarks/sandbox.py @@ -1,4 +1,6 @@ # pylint: skip-file +from math import nan + import numpy as np from banana import register @@ -25,6 +27,7 @@ } nnpdf_base_theory = { "Qref": 91.2, + "QrefQED": nan, "mc": 1.51, "mb": 4.92, "mt": 172.5,