88
99from collections import OrderedDict , namedtuple
1010from collections .abc import Sequence
11- import itertools
1211import logging
1312
1413import numpy as np
1514import pandas as pd
1615import scipy .linalg as la
1716
18- from NNPDF import ThPredictions , CommonData , Experiment
17+ from NNPDF import CommonData
1918from reportengine .checks import require_one , remove_outer , check_not_empty
2019from reportengine .table import table
2120from reportengine import collect
3534 calc_phi ,
3635 bootstrap_values ,
3736)
37+ from validphys .convolution import (
38+ predictions ,
39+ central_predictions ,
40+ PredictionsRequireCutsError ,
41+ )
42+
3843
3944log = logging .getLogger (__name__ )
4045
@@ -45,10 +50,16 @@ class Result:
4550
4651# TODO: Eventually,only one of (NNPDFDataResult, StatsResult) should survive
4752class NNPDFDataResult (Result ):
48- """A result fills its values from a libnnpf data object"""
53+ """A result fills its values from a pandas dataframe
54+ For legacy (libNNPDF) compatibility, falls back to libNNPDF attributes"""
4955
50- def __init__ (self , dataobj ):
51- self ._central_value = dataobj .get_cv ()
56+ def __init__ (self , dataobj = None , central_value = None ):
57+ # This class is used by both validphys and libNNPDF objects
58+ # when central_value is not explictly passed, fallback to
59+ # libNNPDF object .get_cv()
60+ if central_value is None :
61+ central_value = dataobj .get_cv ()
62+ self ._central_value = np .array (central_value ).reshape (- 1 )
5263
5364 @property
5465 def central_value (self ):
@@ -72,8 +83,8 @@ def std_error(self):
7283
7384
7485class DataResult (NNPDFDataResult ):
75- def __init__ (self , dataobj , covmat , sqrtcovmat ):
76- super ().__init__ (dataobj )
86+ def __init__ (self , dataobj , covmat , sqrtcovmat , central_value = None ):
87+ super ().__init__ (dataobj , central_value = central_value )
7788 self ._covmat = covmat
7889 self ._sqrtcovmat = sqrtcovmat
7990
@@ -96,12 +107,22 @@ def sqrtcovmat(self):
96107
97108
98109class ThPredictionsResult (NNPDFDataResult ):
99- def __init__ (self , dataobj , stats_class , label = None ):
110+ """Class holding theory prediction
111+ For legacy purposes it still accepts libNNPDF datatypes, but prefers python-pure stuff
112+ """
113+ def __init__ (self , dataobj , stats_class , label = None , central_value = None ):
100114 self .stats_class = stats_class
101115 self .label = label
102- self ._std_error = dataobj .get_error ()
103- self ._rawdata = dataobj .get_data ()
104- super ().__init__ (dataobj )
116+ # Ducktype the input into numpy arrays
117+ try :
118+ self ._rawdata = dataobj .to_numpy ()
119+ # If the numpy conversion worked then we don't have a libNNPDF in our hands
120+ stats = stats_class (self ._rawdata .T )
121+ self ._std_error = stats .std_error ()
122+ except AttributeError :
123+ self ._std_error = dataobj .get_error ()
124+ self ._rawdata = dataobj .get_data ()
125+ super ().__init__ (dataobj , central_value = central_value )
105126
106127 @property
107128 def std_error (self ):
@@ -123,16 +144,28 @@ def make_label(pdf, dataset):
123144 return label
124145
125146 @classmethod
126- def from_convolution (cls , pdf , dataset , loaded_pdf = None , loaded_data = None ):
127- if loaded_pdf is None :
128- loaded_pdf = pdf .load ()
129- if loaded_data is None :
130- loaded_data = dataset .load ()
131- th_predictions = ThPredictions (loaded_pdf , loaded_data )
147+ def from_convolution (cls , pdf , dataset ):
148+ # This should work for both single dataset and whole groups
149+ try :
150+ datasets = dataset .datasets
151+ except AttributeError :
152+ datasets = (dataset ,)
153+
154+ try :
155+ all_preds = []
156+ all_centrals = []
157+ for d in datasets :
158+ all_preds .append (predictions (d , pdf ))
159+ all_centrals .append (central_predictions (d , pdf ))
160+ except PredictionsRequireCutsError as e :
161+ raise PredictionsRequireCutsError ("Predictions from FKTables always require cuts, "
162+ "if you want to use the fktable intrinsic cuts set `use_cuts: 'internal'`" ) from e
163+ th_predictions = pd .concat (all_preds )
164+ central_values = pd .concat (all_centrals )
132165
133166 label = cls .make_label (pdf , dataset )
134167
135- return cls (th_predictions , pdf .stats_class , label )
168+ return cls (th_predictions , pdf .stats_class , label , central_value = central_values )
136169
137170
138171class PositivityResult (StatsResult ):
@@ -454,7 +487,7 @@ def results(dataset: (DataSetSpec), pdf: PDF, covariance_matrix, sqrt_covmat):
454487 data = dataset .load ()
455488 return (
456489 DataResult (data , covariance_matrix , sqrt_covmat ),
457- ThPredictionsResult .from_convolution (pdf , dataset , loaded_data = data ),
490+ ThPredictionsResult .from_convolution (pdf , dataset ),
458491 )
459492
460493
@@ -480,13 +513,9 @@ def pdf_results(
480513 """Return a list of results, the first for the data and the rest for
481514 each of the PDFs."""
482515
483- data = dataset .load ()
484- th_results = []
485- for pdf in pdfs :
486- th_result = ThPredictionsResult .from_convolution (pdf , dataset , loaded_data = data )
487- th_results .append (th_result )
516+ th_results = [ThPredictionsResult .from_convolution (pdf , dataset ) for pdf in pdfs ]
488517
489- return (DataResult (data , covariance_matrix , sqrt_covmat ), * th_results )
518+ return (DataResult (dataset . load () , covariance_matrix , sqrt_covmat ), * th_results )
490519
491520
492521@require_one ("pdfs" , "pdf" )
0 commit comments