jelli 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
jelli-0.0.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Aleks Smolkovič & Peter Stangl
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
jelli-0.0.1/PKG-INFO ADDED
@@ -0,0 +1,20 @@
1
+ Metadata-Version: 2.4
2
+ Name: jelli
3
+ Version: 0.0.1
4
+ Summary: A JAX-based EFT likelihood.
5
+ Author-email: Aleks Smolkovič <aleks.smolkovic@ijs.si>, Peter Stangl <peter.stangl@cern.ch>
6
+ License: MIT
7
+ Requires-Python: >=3.8
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: numpy
11
+ Requires-Dist: scipy
12
+ Requires-Dist: jax
13
+ Requires-Dist: wilson
14
+ Requires-Dist: rgevolve
15
+ Requires-Dist: multipledispatch
16
+ Requires-Dist: networkx
17
+ Requires-Dist: h5py
18
+ Dynamic: license-file
19
+
20
+ # jelli - a JAX-based EFT Likelihood
jelli-0.0.1/README.md ADDED
@@ -0,0 +1 @@
1
+ # jelli - a JAX-based EFT Likelihood
@@ -0,0 +1,32 @@
1
+ [build-system]
2
+ requires = ["setuptools >= 61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "jelli"
7
+ authors = [
8
+ {name = "Aleks Smolkovič", email = "aleks.smolkovic@ijs.si"},
9
+ {name = "Peter Stangl", email = "peter.stangl@cern.ch"},
10
+ ]
11
+ description = "A JAX-based EFT likelihood."
12
+ readme = "README.md"
13
+ requires-python = ">=3.8"
14
+ license = {text = "MIT"}
15
+ dependencies = [
16
+ "numpy",
17
+ "scipy",
18
+ "jax",
19
+ "wilson",
20
+ "rgevolve",
21
+ "multipledispatch",
22
+ "networkx",
23
+ "h5py",
24
+ ]
25
+ dynamic = ["version"]
26
+
27
+ [tool.setuptools.packages.find]
28
+ where = ["src/"]
29
+
30
+ [tool.setuptools.dynamic]
31
+ version = {attr = "jelli._version.__version__"}
32
+
jelli-0.0.1/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
jelli-0.0.1/setup.py ADDED
@@ -0,0 +1,3 @@
1
+ from setuptools import setup
2
+
3
+ setup()
@@ -0,0 +1,5 @@
1
+ from ._version import __version__
2
+ from .core.global_likelihood import GlobalLikelihood
3
+ from .core.observable_sector import ObservableSector
4
+ from .core.measurement import Measurement
5
+ from .core.custom_basis import CustomBasis
@@ -0,0 +1 @@
1
+ __version__ = '0.0.1'
File without changes
@@ -0,0 +1,177 @@
1
+ from typing import List, Dict, Union
2
+
3
+ class CustomBasis:
4
+ '''
5
+ A class to represent a custom parameter basis.
6
+
7
+ Parameters
8
+ ----------
9
+ name : str
10
+ The name of the custom basis.
11
+ parameters : list or dict
12
+ The parameters in the custom basis. If a list is provided, all parameters are assumed to be real. If a dict is provided, the keys are the parameter names and the values are the types (e.g., 'R' for real, 'C' for complex).
13
+
14
+ Attributes
15
+ ----------
16
+ name : str
17
+ The name of the custom basis.
18
+ parameters : dict
19
+ The parameters in the custom basis. The keys are the parameter names and the values are the types (e.g., 'R' for real, 'C' for complex).
20
+
21
+ Methods
22
+ -------
23
+ get_all_names() -> List[str]
24
+ Get all custom basis names.
25
+ get(name: str) -> 'CustomBasis'
26
+ Get a custom basis by name.
27
+ get_all() -> List['CustomBasis']
28
+ Get all custom basis objects.
29
+ get_parameter_basis() -> List
30
+ Get the parameter basis.
31
+
32
+ Examples
33
+ --------
34
+ Initialize a custom basis with real parameters:
35
+
36
+ >>> custom_basis = CustomBasis('example_basis', ['param1', 'param2'])
37
+
38
+ Initialize a custom basis with mixed parameters:
39
+
40
+ >>> custom_basis = CustomBasis('example_basis', {'param1': 'R', 'param2': 'C'})
41
+
42
+ Get all custom basis names:
43
+
44
+ >>> CustomBasis.get_all_names()
45
+
46
+ Get a custom basis by name:
47
+
48
+ >>> custom_basis = CustomBasis.get('example_basis')
49
+
50
+ Get all custom basis objects:
51
+
52
+ >>> custom_bases = CustomBasis.get_all()
53
+
54
+ Get the parameter basis:
55
+
56
+ >>> parameter_basis = custom_basis.get_parameter_basis()
57
+
58
+ '''
59
+
60
+ _custom_bases: Dict[str, 'CustomBasis'] = {} # Class attribute to store all custom bases
61
+
62
+ def __init__(self, name: str, parameters: Union[List[str], Dict[str, str]]):
63
+ """
64
+ Initialize the CustomBasis class.
65
+
66
+ Parameters
67
+ ----------
68
+ name : str
69
+ The name of the custom basis.
70
+ parameters : list or dict
71
+ The parameters in the custom basis. If a list is provided, all parameters are assumed to be real. If a dict is provided, the keys are the parameter names and the values are the types (e.g., 'R' for real, 'C' for complex).
72
+
73
+ Returns
74
+ --------
75
+ None
76
+
77
+ Examples
78
+ --------
79
+ Initialize a custom basis with real parameters:
80
+
81
+ >>> custom_basis = CustomBasis('example_basis', ['param1', 'param2'])
82
+
83
+ Initialize a custom basis with mixed parameters:
84
+
85
+ >>> custom_basis = CustomBasis('example_basis', {'param1': 'R', 'param2': 'C'})
86
+ """
87
+ self.name = name
88
+ if isinstance(parameters, list):
89
+ self.parameters = {param: 'R' for param in parameters}
90
+ elif isinstance(parameters, dict):
91
+ if not all(value in ['R', 'C'] for value in parameters.values()):
92
+ raise ValueError("All parameter types must be either 'R' or 'C'.")
93
+ self.parameters = parameters
94
+ else:
95
+ raise ValueError("Parameters must be a list or a dictionary.")
96
+ self._custom_bases[self.name] = self
97
+
98
+ @classmethod
99
+ def get_all_names(cls) -> List[str]:
100
+ """
101
+ Get all custom basis names.
102
+
103
+ Returns
104
+ --------
105
+ list
106
+ A list of all custom basis names.
107
+
108
+ Examples
109
+ --------
110
+ >>> CustomBasis.get_all_names()
111
+ """
112
+ return sorted(cls._custom_bases.keys())
113
+
114
+ @classmethod
115
+ def get(cls, name: str) -> 'CustomBasis':
116
+ """
117
+ Get a custom basis by name.
118
+
119
+ Parameters
120
+ ----------
121
+ name : str
122
+ The name of the custom basis.
123
+ Returns
124
+ --------
125
+ CustomBasis
126
+ The custom basis object.
127
+
128
+ Examples
129
+ --------
130
+ >>> CustomBasis.get('example_basis')
131
+ """
132
+ return cls._custom_bases.get(name)
133
+
134
+ @classmethod
135
+ def get_all(cls) -> List['CustomBasis']:
136
+ """
137
+ Get all custom basis objects.
138
+
139
+ Returns
140
+ --------
141
+ list
142
+ A list of all custom basis objects.
143
+
144
+ Examples
145
+ --------
146
+ >>> CustomBasis.get_all()
147
+ """
148
+ return list(cls._custom_bases.values())
149
+
150
+ def get_parameter_basis(self, split_re_im=True) -> List:
151
+ """
152
+ Get the parameter basis.
153
+
154
+ Parameters
155
+ ----------
156
+ split_re_im : bool, optional
157
+ If True, split parameters into real and imaginary parts, otherwise return the parameters directly. Default is True.
158
+
159
+ Returns
160
+ --------
161
+ list
162
+ A list containing the parameter basis.
163
+
164
+ Examples
165
+ --------
166
+ >>> custom_basis = CustomBasis('example_basis', ['param1', 'param2'])
167
+ >>> custom_basis.get_parameter_basis('example_basis')
168
+ """
169
+ if split_re_im:
170
+ parameter_basis = []
171
+ for parameter, parameter_type in self.parameters.items():
172
+ parameter_basis.append((parameter, 'R'))
173
+ if parameter_type == 'C':
174
+ parameter_basis.append((parameter, 'I'))
175
+ else:
176
+ parameter_basis = self.parameters.keys()
177
+ return sorted(parameter_basis)
@@ -0,0 +1,100 @@
1
+ from typing import Dict, Optional, Iterable
2
+ import h5py
3
+ import numpy as np
4
+ from jax import numpy as jnp
5
+ import os
6
+ from ..utils.data_io import get_json_schema, hash_observable_names
7
+
8
+ class ExperimentalCorrelations:
9
+
10
+ _instances: Dict[str, Dict[str, 'ExperimentalCorrelations']] = {
11
+ 'correlations': {},
12
+ 'central': {},
13
+ 'uncertainties': {}
14
+ } # Dictionary to hold instances of ExperimentalCorrelations
15
+ _covariance_scaled: Dict[str, jnp.ndarray] = {}
16
+ _pdfxf_h5_versions = {'1.0'} # Set of supported versions of the pdfxf-h5 JSON schema
17
+
18
+ def __init__(
19
+ self,
20
+ hash_val: str,
21
+ data_type: str,
22
+ data: np.ndarray,
23
+ row_names: Dict[str, int],
24
+ col_names: Dict[str, int]
25
+ ) -> None:
26
+ self.hash_val = hash_val
27
+ self.data_type = data_type
28
+ self.data = data
29
+ self.row_names = row_names
30
+ self.col_names = col_names
31
+ self._instances[data_type][hash_val] = self
32
+
33
+ @classmethod
34
+ def _load_file(cls, path: str) -> None:
35
+ with h5py.File(path, 'r') as f:
36
+ schema_name, schema_version = get_json_schema(dict(f.attrs))
37
+ if schema_name == 'pdfxf-h5' and schema_version in cls._pdfxf_h5_versions:
38
+ for hash_val in f['correlations']:
39
+ cls.from_hdf5_group(hash_val, 'correlations', f['correlations'][hash_val])
40
+ for hash_val in f['central']:
41
+ cls.from_hdf5_group(hash_val, 'central', f['central'][hash_val])
42
+ for hash_val in f['uncertainties']:
43
+ cls.from_hdf5_group(hash_val, 'uncertainties', f['uncertainties'][hash_val])
44
+
45
+ @classmethod
46
+ def load(cls, path: str) -> None:
47
+ # load all hdf5 files in the directory
48
+ if os.path.isdir(path):
49
+ for file in os.listdir(path):
50
+ if file.endswith('.hdf5'):
51
+ cls._load_file(os.path.join(path, file))
52
+ # load single hdf5 file
53
+ else:
54
+ cls._load_file(path)
55
+
56
+ @classmethod
57
+ def from_hdf5_group(cls, hash_val: str, data_type: str, hdf5_group: h5py.Group) -> None:
58
+ data = hdf5_group['data']
59
+ data = np.array(data[()], dtype=np.float64) * data.attrs.get('scale', 1.0)
60
+ row_names = {name: i for i, name in enumerate(hdf5_group['row_names'][()].astype(str))}
61
+ col_names = {name: i for i, name in enumerate(hdf5_group.get('col_names', np.array([]))[()].astype(str))}
62
+ cls(hash_val, data_type, data, row_names, col_names)
63
+
64
+ @classmethod
65
+ def get_data(
66
+ cls,
67
+ data_type: str,
68
+ row_names: Iterable[str],
69
+ col_names: Optional[Iterable[str]] = []
70
+ ):
71
+ hash_val = hash_observable_names(row_names, col_names)
72
+ if hash_val in cls._instances[data_type]:
73
+ data = cls._instances[data_type][hash_val].data
74
+ else:
75
+ hash_val = hash_observable_names(col_names, row_names)
76
+ if hash_val in cls._instances[data_type]:
77
+ data = cls._instances[data_type][hash_val].data.T
78
+ else:
79
+ data = None
80
+ return data
81
+
82
+ @classmethod
83
+ def get_cov_scaled(
84
+ cls,
85
+ row_names: Iterable[str],
86
+ col_names: Iterable[str],
87
+ std_exp_scaled_row: np.ndarray,
88
+ std_exp_scaled_col: np.ndarray,
89
+ ):
90
+ hash_val = hash_observable_names(row_names, col_names)
91
+ if hash_val in cls._covariance_scaled:
92
+ cov_scaled = cls._covariance_scaled[hash_val]
93
+ else:
94
+ corr = cls.get_data('correlations', row_names, col_names)
95
+ if corr is None:
96
+ raise ValueError(f"Correlation data for {row_names} and {col_names} not found.")
97
+ cov_scaled = corr * np.outer(std_exp_scaled_row, std_exp_scaled_col)
98
+ cov_scaled = jnp.array(cov_scaled, dtype=jnp.float64)
99
+ cls._covariance_scaled[hash_val] = cov_scaled
100
+ return cov_scaled