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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions extras/cc_nnlo_ic/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Kirill
2601.02916*.pdf
CF_HQI_CC_DIS
# GPL
0410259*.pdf
Multiple-Polylogarithm
4 changes: 4 additions & 0 deletions extras/cc_nnlo_ic/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# CC NNLO intrinsic
Implementation of [2601.02916](https://arxiv.org/abs/2601.02916).

Clone the results repo here: `git clone [email protected]:Qdashkin/CF_HQI_CC_DIS.git`.
71 changes: 71 additions & 0 deletions extras/cc_nnlo_ic/mma.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
"""Python interface to Mathematica via the CLI."""

import io
import shutil
import subprocess
import threading
from typing import Self


def _thread_writer(p: subprocess.Popen, res: io.StringIO) -> None:
"""
Thread worker.

Reads from the stdout of the subprocess until it is closed or an `@` is
encountered.

Parameters
----------
p : subprocess.Popen
subprocess
res : stream
stream to which the output is copied
"""
while True:
# print("read data: ")
data = p.stdout.read(1).decode("utf-8")
if not data or data == "@":
break
res.write(data)
res.flush()


class MmaRunner:
"""Call Mathematica interactively from Python via the CLI."""

p: subprocess.Popen
"""CLI thread"""

def __enter__(self: Self) -> Self:
path = shutil.which("math")
assert path is not None
self.p = subprocess.Popen(
[path, "-noprompt"], stdin=subprocess.PIPE, stdout=subprocess.PIPE
)
return self

def send(self: Self, code: str) -> str:
"""Send Mathematica code to the running CLI.

An explicit `@` is added as the EOF signal to the thread worker.
"""
# reading needs to be on a seperate stream - idea from here:
# https://stackoverflow.com/questions/19880190/interactive-input-output-using-python/53312631#53312631
stream = io.StringIO()
writer = threading.Thread(target=_thread_writer, args=(self.p, stream))
writer.start()

self.p.stdin.write((code + 'Print["@"];\n').encode())
self.p.stdin.flush()
writer.join()
stream.seek(0)
s = stream.read()
return s[1:-2].strip()

def __exit__(self: Self, exc_type: type, _exc_value, _traceback):
"""Close the interactive CLI, by sending the `Exit[]` command."""
if exc_type is not None:
return
if self.p is not None:
self.send("Exit[];")
self.p.terminate()
64 changes: 64 additions & 0 deletions extras/cc_nnlo_ic/run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"""Check and import Kirill."""

import numpy as np
from mma import MmaRunner

from yadism.coefficient_functions.intrinsic import f2_cc

PATH = "CF_HQI_CC_DIS/strFun.mx"

INIT = f'Get["{PATH}"];' + r"""
colorRule = {cF -> 4/3, cA -> 3}
ruleU = {u -> Sqrt[y1/(y1 - 4 y2)]};
loResult = {Splus/2, (Splus xBj)/(1 - \[Beta]), 2 Rplus};
simpleGRule = {G[0,x_] :> Log[x]};
y2SingularRule = {y2 -> m2/(Q2+m2)};
"""


class MockESF:
Q2: float


def check_lo(r: MmaRunner) -> None:
"""Check LO is a delta function."""
for sf in [1, 2, 3]:
lo = r.send(rf"Print[L[{sf}, 0, 1] /. LCoefRules];")
print(f"F_{sf}|LO = {lo} which is {lo == 'PD[0]'}")


def check_nlo(r: MmaRunner) -> None:
"""Check NLO."""
Q2 = 1.0
m2 = 1.0
nf = 3
esf = MockESF()
esf.Q2 = Q2
yad = f2_cc.Splus(esf, nf, m1sq=m2)
# x = 0.1
# yad_lo = yad.LO()
# print(yad_lo.loc(x,yad_lo.args["loc"]))
yad_nlo = yad.NLO()
z = 0.1
# TODO: there is a magic 2 still
yad_nlo_sing = yad_nlo.sing(z, yad_nlo.args["sing"]) / yad.lo / 2.0
sf = 2
kk_nlo_sing = r.send(rf"""
Block[{{sf={sf},cf,sing}},
cf = cF L[sf, 1, 1] /. LCoefRules;
sing = Coefficient[cf,PD@2] * Log[1-z]/(1-z) + Coefficient[cf,PD@1] * 1/(1-z);
sing = sing /. colorRule /. simpleGRule /. y2SingularRule /. {{m2 -> {m2}, Q2 -> {Q2}, z -> {z}}};
Print@sing;
]""")
kk_nlo_sing = float(kk_nlo_sing)
print(
f"F_{sf}|NLO|sing = {kk_nlo_sing} which is {np.isclose(kk_nlo_sing, yad_nlo_sing)}"
)


if __name__ == "__main__":
# initialize
with MmaRunner() as runner:
runner.send(INIT)
# check_lo(runner)
check_nlo(runner)