Python
import numpy as np

# PQ stuff

paper_white_nits = 0.5 # still no idea what this is, but it's some kind of dial
bt709_scale_factor = 59.5208

m1 = 0.1593017578125
m2 = 78.84375
c1 = 0.8359375
c2 = 18.8515625
c3 = 18.6875

def bt1886_eotf(x):
    return 100. * pow(x, 2.4)

def bt709_oetf(x):
    s1 = x * 4.5
    s2 = pow(x, 1. / 2.2) * 1.099 - 0.099
    return s1 if x <= 0.018 else s2

def pq_ootf(x):
    return bt1886_eotf(bt709_oetf(x * bt709_scale_factor))
    
def pq_inv_eotf(x):
    ym = pow(x, m1);
    return pow((c1 + c2 * ym) / (1.0 + c3 * ym), m2)

# color stuff

mat_srgb_to_bt2020 = [
    [ 0.627403896,  0.329283039,  0.0433130657],
    [ 0.0690972894, 0.919540395,  0.0113623156],
    [ 0.0163914389, 0.0880133077, 0.895595253]]
    
# womp womp

srgb_lin_val = [2.56, 0.15, 0.85]
rec2020_lin_val = np.matmul(mat_srgb_to_bt2020, srgb_lin_val)
pq_val = [pq_inv_eotf(pq_ootf(x) * (paper_white_nits /10000.)) for x in rec2020_lin_val] 

print("scene-linear sRGB color", srgb_lin_val)
print("scene-linear Rec. 2020 value:", rec2020_lin_val)
print("PQ value:", pq_val)
scene-linear sRGB color [2.56, 0.15, 0.85]
scene-linear Rec. 2020 value: [1.69236254 0.32447809 0.81642004]
PQ value: [0.993009117897618, 0.7958803037537535, 0.9072875475319419]