fastlisaresponse 1.1.14__cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

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.
@@ -0,0 +1 @@
1
+ from .utility import get_overlap
@@ -0,0 +1,356 @@
1
+ # Collection of citations for modules in GBGPU package
2
+
3
+ """
4
+ :code:`gbgpu.utils.citations`:
5
+
6
+ This module is used to collect citations for all modules in the package. This
7
+ module is then imported to add citations to module classes using their :code:`citation`
8
+ attribute.
9
+ """
10
+
11
+ import abc
12
+ import enum
13
+ from typing import Iterable, List, Optional, Union
14
+
15
+ from pydantic import BaseModel
16
+
17
+ from gbgpu.utils.exceptions import InvalidInputFile
18
+
19
+
20
+ def _hyphen_replace(field: str) -> str:
21
+ return field.replace("_", "-")
22
+
23
+
24
+ class HyphenUnderscoreAliasModel(BaseModel):
25
+ """Pydantic model were hyphen replace underscore in field names."""
26
+
27
+ class Config:
28
+ alias_generator = _hyphen_replace
29
+ extra = "ignore"
30
+ frozen = True
31
+
32
+
33
+ class Author(HyphenUnderscoreAliasModel):
34
+ """Description of a reference author."""
35
+
36
+ family_names: str
37
+ given_names: str
38
+ orcid: Optional[str] = None
39
+ affiliation: Optional[str] = None
40
+ email: Optional[str] = None
41
+
42
+
43
+ class Publisher(HyphenUnderscoreAliasModel):
44
+ """Description of a publisher."""
45
+
46
+ name: str
47
+
48
+
49
+ class Identifier(HyphenUnderscoreAliasModel):
50
+ """Description of an identifier."""
51
+
52
+ type: str
53
+ value: str
54
+ description: Optional[str] = None
55
+
56
+
57
+ class ReferenceABC(HyphenUnderscoreAliasModel, abc.ABC):
58
+ """Abstract base class for references."""
59
+
60
+ @abc.abstractmethod
61
+ def to_bibtex(self) -> str:
62
+ """Convert a reference object to a BibTeX string representation."""
63
+
64
+
65
+ class ArxivIdentifier(BaseModel):
66
+ """Class representing an arXiv identifier"""
67
+
68
+ reference: str
69
+ primary_class: Optional[str] = None
70
+
71
+
72
+ class ArticleReference(ReferenceABC):
73
+ """Description of an article."""
74
+
75
+ abbreviation: str
76
+ authors: List[Author]
77
+ title: str
78
+ journal: Optional[str] = None
79
+ year: int
80
+ month: Optional[int] = None
81
+ issue: Optional[int] = None
82
+ publisher: Optional[Publisher] = None
83
+ pages: Optional[int] = None
84
+ start: Optional[int] = None
85
+ issn: Optional[str] = None
86
+ doi: Optional[str] = None
87
+ identifiers: Optional[List[Identifier]] = None
88
+
89
+ @property
90
+ def arxiv_preprint(self) -> Optional[ArxivIdentifier]:
91
+ """
92
+ Detect an arXiv identifier if any.
93
+
94
+ an arXiv identifier is:
95
+ - an identifier of type "other"
96
+ - which starts with "arxiv:" (case insensitive)
97
+ - whose second part is either:
98
+ - The arXiv reference (e.g. "arxiv:1912.07609")
99
+ - The primary class followed by '/' and the reference (e.g. "arxiv:gr-qc/1912.07609")
100
+ """
101
+ if self.identifiers is None:
102
+ return None
103
+
104
+ for identifier in self.identifiers:
105
+ if identifier.type != "other":
106
+ continue
107
+ if not identifier.value.lower().startswith("arxiv:"):
108
+ continue
109
+ data = identifier.value.lower().removeprefix("arxiv:")
110
+ primary_class, reference = (
111
+ data.split("/", 1) if "/" in data else (None, data)
112
+ )
113
+ return ArxivIdentifier(primary_class=primary_class, reference=reference)
114
+ return None
115
+
116
+ def to_bibtex(self) -> str:
117
+ """Build the BibTeX representation of an article."""
118
+ arxiv_id = self.arxiv_preprint
119
+
120
+ line_format = (
121
+ """ {:<10} = \"{}\"""" if arxiv_id is None else """ {:<13} = \"{}\""""
122
+ )
123
+
124
+ def format_line(key: str, value: str, format: str = line_format) -> str:
125
+ return format.format(key, value)
126
+
127
+ lines = []
128
+ lines.append("@article{" + self.abbreviation)
129
+ lines.append(
130
+ format_line(
131
+ "author",
132
+ " and ".join(
133
+ [
134
+ "{}, {}".format(author.family_names, author.given_names)
135
+ for author in self.authors
136
+ ]
137
+ ),
138
+ )
139
+ )
140
+ lines.append(format_line("title", "{" + self.title + "}"))
141
+ if self.journal is not None:
142
+ lines.append(format_line("journal", self.journal))
143
+ lines.append(format_line("year", str(self.year)))
144
+ if self.month is not None:
145
+ lines.append(format_line("month", str(self.month)))
146
+ if self.issue is not None:
147
+ lines.append(format_line("number", str(self.issue)))
148
+ if self.publisher is not None:
149
+ lines.append(format_line("publisher", str(self.publisher.name)))
150
+ if self.start is not None:
151
+ lines.append(
152
+ format_line(
153
+ "pages",
154
+ str(self.start)
155
+ if self.pages is None
156
+ else "{}--{}".format(self.start, self.start + self.pages),
157
+ )
158
+ )
159
+ if self.issn is not None:
160
+ lines.append(format_line("issn", str(self.issn)))
161
+ if self.doi is not None:
162
+ lines.append(format_line("doi", str(self.doi)))
163
+ if arxiv_id is not None:
164
+ lines.append(format_line("archivePrefix", "arXiv"))
165
+ lines.append(format_line("eprint", arxiv_id.reference))
166
+ if arxiv_id.primary_class is not None:
167
+ lines.append(format_line("primaryClass", arxiv_id.primary_class))
168
+
169
+ return ",\n".join(lines) + "\n}"
170
+
171
+
172
+ class SoftwareReference(ReferenceABC):
173
+ """Description of a Software"""
174
+
175
+ authors: list[Author]
176
+ title: str
177
+
178
+ license: Optional[str] = None
179
+ url: Optional[str] = None
180
+ repository: Optional[str] = None
181
+ identifiers: Optional[List[Identifier]] = None
182
+ year: Optional[int] = None
183
+ month: Optional[int] = None
184
+ version: Optional[str] = None
185
+
186
+ @property
187
+ def doi(self) -> Optional[str]:
188
+ """Return the first DOI in identifiers if any"""
189
+ if self.identifiers is None:
190
+ return None
191
+
192
+ for identifier in self.identifiers:
193
+ if identifier.type == "doi":
194
+ return identifier.value
195
+ return None
196
+
197
+ def to_bibtex(self) -> str:
198
+ """Build the BibTeX representation of a software."""
199
+
200
+ def format_line(key: str, value: str) -> str:
201
+ return """ {:<10} = \"{}\"""".format(key, value)
202
+
203
+ lines = []
204
+ lines.append("@software{" + self.title)
205
+ lines.append(
206
+ format_line(
207
+ "author",
208
+ " and ".join(
209
+ [
210
+ "{}, {}".format(author.family_names, author.given_names)
211
+ for author in self.authors
212
+ ]
213
+ ),
214
+ )
215
+ )
216
+ lines.append(format_line("title", "{" + self.title + "}"))
217
+ if self.license is not None:
218
+ lines.append(format_line("license", self.license))
219
+ if self.url is not None:
220
+ lines.append(format_line("url", self.url))
221
+ if self.repository is not None:
222
+ lines.append(format_line("repository", self.repository))
223
+ if self.year is not None:
224
+ lines.append(format_line("year", str(self.year)))
225
+ if self.month is not None:
226
+ lines.append(format_line("month", str(self.month)))
227
+ if self.version is not None:
228
+ lines.append(format_line("version", self.version))
229
+ if self.doi is not None:
230
+ lines.append(format_line("doi", str(self.doi)))
231
+
232
+ return ",\n".join(lines) + "\n}"
233
+
234
+
235
+ Reference = Union[ArticleReference, SoftwareReference]
236
+
237
+
238
+ class REFERENCE(enum.Enum):
239
+ FAST_GB = "Cornish:2007if"
240
+ TRIPLES_1 = "Robson:2018svj"
241
+ GBGPU_SOFTWARE = "michael_l_katz_2022_6500434"
242
+
243
+ def __str__(self) -> str:
244
+ return str(self.value)
245
+
246
+
247
+ class CitationRegistry:
248
+ __slots__ = "registry"
249
+
250
+ registry: dict[str, Reference]
251
+
252
+ def __init__(self, **kwargs):
253
+ self.registry = kwargs
254
+
255
+ def get(self, key: Union[str, REFERENCE]) -> Reference:
256
+ """Return a Reference object from its key."""
257
+ return self.registry[key if isinstance(key, str) else key.value]
258
+
259
+ def all(self) -> Iterable[Reference]:
260
+ return self.registry.values()
261
+
262
+
263
+ def build_citation_registry() -> CitationRegistry:
264
+ """Read the package CITATION.cff and build the corresponding registry."""
265
+ import json
266
+ import pathlib
267
+
268
+ import jsonschema
269
+ import yaml
270
+
271
+ from gbgpu import __file__ as _gbgpu_root_file
272
+ from gbgpu import _is_editable as is_editable
273
+
274
+ gbgpu_root = pathlib.Path(_gbgpu_root_file).parent
275
+ cff_root = gbgpu_root.parent.parent if is_editable else gbgpu_root
276
+ citation_cff_path = cff_root / "CITATION.cff"
277
+
278
+ with open(citation_cff_path, "rt") as fid:
279
+ cff = yaml.safe_load(fid)
280
+
281
+ with open(pathlib.Path(__file__).parent / "cff_1_2_0.schema.json", "r") as f:
282
+ cff_schema = json.load(f)
283
+
284
+ try:
285
+ jsonschema.validate(cff, cff_schema)
286
+ except jsonschema.SchemaError as e:
287
+ raise InvalidInputFile("cff_1_2_0.schema.json is not a valid schema.") from e
288
+ except jsonschema.exceptions.ValidationError as e:
289
+ raise InvalidInputFile(
290
+ "The file {} does not match its expected schema. Contact gbgpu developers.".format(
291
+ citation_cff_path
292
+ )
293
+ ) from e
294
+
295
+ def to_reference(ref_dict) -> Reference:
296
+ if ref_dict["type"] == "article":
297
+ return ArticleReference(**ref_dict)
298
+ if ref_dict["type"] == "software":
299
+ return SoftwareReference(**ref_dict)
300
+
301
+ raise InvalidInputFile(
302
+ "The file {} contains references whose type ({}) ".format(
303
+ citation_cff_path, ref_dict["type"]
304
+ )
305
+ + "is not supported",
306
+ ref_dict,
307
+ )
308
+
309
+ references = {ref["abbreviation"]: to_reference(ref) for ref in cff["references"]}
310
+
311
+ return CitationRegistry(**references, **{cff["title"]: to_reference(cff)})
312
+
313
+
314
+ COMMON_REFERENCES = [
315
+ REFERENCE.FAST_GB,
316
+ REFERENCE.TRIPLES_1,
317
+ REFERENCE.GBGPU_SOFTWARE,
318
+ ]
319
+
320
+
321
+ class Citable:
322
+ """Base class for classes associated with specific citations."""
323
+
324
+ registry: Optional[CitationRegistry] = None
325
+
326
+ @classmethod
327
+ def citation(cls) -> str:
328
+ """Return the module references as a printable BibTeX string."""
329
+ references = cls.module_references()
330
+
331
+ registry = cls._get_registry()
332
+
333
+ bibtex_entries = [registry.get(str(key)).to_bibtex() for key in references]
334
+ return "\n\n".join(bibtex_entries)
335
+
336
+ @classmethod
337
+ def all_citations(cls) -> str:
338
+ """Return all the citations from the registry as printable BibTeX string"""
339
+ registry = cls._get_registry()
340
+
341
+ bibtex_entries = [entry.to_bibtex() for entry in registry.all()]
342
+ return "\n\n".join(bibtex_entries)
343
+
344
+ @classmethod
345
+ def module_references(cls) -> Iterable[Union[REFERENCE, str]]:
346
+ """Method implemented by each class to define its list of references"""
347
+ return COMMON_REFERENCES
348
+
349
+ @classmethod
350
+ def _get_registry(cls) -> CitationRegistry:
351
+ if Citable.registry is None:
352
+ from gbgpu import get_logger
353
+
354
+ get_logger().debug("Building the Citation Registry from CITATION.cff")
355
+ Citable.registry = build_citation_registry()
356
+ return Citable.registry