mapFolding 0.3.7__py3-none-any.whl → 0.3.8__py3-none-any.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.
- citations/constants.py +3 -0
- citations/updateCitation.py +202 -86
- {mapFolding-0.3.7.dist-info → mapFolding-0.3.8.dist-info}/METADATA +1 -1
- mapFolding-0.3.8.dist-info/RECORD +26 -0
- someAssemblyRequired/makeJob.py +35 -7
- someAssemblyRequired/{synthesizeModuleJob.py → synthesizeModuleJobNumba.py} +81 -27
- someAssemblyRequired/synthesizeModulesNumba.py +446 -0
- syntheticModules/__init__.py +3 -4
- syntheticModules/{Initialize.py → numbaInitialize.py} +6 -3
- syntheticModules/{Parallel.py → numbaParallel.py} +10 -6
- syntheticModules/{Sequential.py → numbaSequential.py} +4 -4
- mapFolding-0.3.7.dist-info/RECORD +0 -25
- someAssemblyRequired/synthesizeModules.py +0 -216
- {mapFolding-0.3.7.dist-info → mapFolding-0.3.8.dist-info}/WHEEL +0 -0
- {mapFolding-0.3.7.dist-info → mapFolding-0.3.8.dist-info}/entry_points.txt +0 -0
- {mapFolding-0.3.7.dist-info → mapFolding-0.3.8.dist-info}/top_level.txt +0 -0
citations/constants.py
ADDED
citations/updateCitation.py
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
from cffconvert.cli.create_citation import create_citation
|
|
2
|
+
from mapFolding.citations.constants import GITHUB_API_VERSION_HEADER
|
|
2
3
|
from packaging.metadata import Metadata as PyPAMetadata
|
|
3
4
|
from typing import Any, Dict, List
|
|
4
5
|
import attrs
|
|
5
6
|
import cffconvert
|
|
6
|
-
import
|
|
7
|
+
import os
|
|
7
8
|
import packaging
|
|
8
9
|
import packaging.metadata
|
|
9
10
|
import packaging.utils
|
|
10
11
|
import packaging.version
|
|
11
12
|
import pathlib
|
|
13
|
+
import requests
|
|
12
14
|
import ruamel.yaml
|
|
13
15
|
import tomli
|
|
14
16
|
|
|
@@ -23,7 +25,7 @@ Tentative plan:
|
|
|
23
25
|
- pathFilenameCitationDOTcffRepo
|
|
24
26
|
- Commit and push to GitHub
|
|
25
27
|
- this complicates things
|
|
26
|
-
- I want the updated citation to be in the `commit` field of itself
|
|
28
|
+
- I want the updated citation to be in the `commit` field of itself: but the commit field isn't even working right now
|
|
27
29
|
"""
|
|
28
30
|
|
|
29
31
|
@attrs.define
|
|
@@ -31,28 +33,30 @@ class CitationNexus:
|
|
|
31
33
|
"""
|
|
32
34
|
- one-to-one correlation with `cffconvert.lib.cff_1_2_x.citation` class Citation_1_2_x.cffobj
|
|
33
35
|
"""
|
|
34
|
-
cffDASHversion: str
|
|
35
|
-
message: str
|
|
36
|
-
|
|
37
|
-
abstract: str | None = None
|
|
38
|
-
authors: list[dict[str,str]] = attrs.field(factory=list)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
repository: str | None = None
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
36
|
+
cffDASHversion: str
|
|
37
|
+
message: str
|
|
38
|
+
|
|
39
|
+
abstract: str | None = None
|
|
40
|
+
authors: list[dict[str,str]] = attrs.field(factory=list)
|
|
41
|
+
# GitHub TODO
|
|
42
|
+
commit: str | None = None
|
|
43
|
+
contact: list[dict[str,str]] = attrs.field(factory=list)
|
|
44
|
+
dateDASHreleased: str | None = None
|
|
45
|
+
doi: str | None = None
|
|
46
|
+
identifiers: list[str] = attrs.field(factory=list)
|
|
47
|
+
keywords: list[str] = attrs.field(factory=list)
|
|
48
|
+
license: str | None = None
|
|
49
|
+
licenseDASHurl: str | None = None
|
|
50
|
+
preferredDASHcitation: str | None = None
|
|
51
|
+
# TODO bibtex files in pathCitationSSOT. Conversion method and timing TBD.
|
|
52
|
+
references: list[str] = attrs.field(factory=list)
|
|
53
|
+
repository: str | None = None
|
|
54
|
+
repositoryDASHartifact: str | None = None
|
|
55
|
+
repositoryDASHcode: str | None = None
|
|
56
|
+
title: str | None = None
|
|
57
|
+
type: str | None = None
|
|
58
|
+
url: str | None = None
|
|
59
|
+
version: str | None = None
|
|
56
60
|
|
|
57
61
|
def setInStone(self, prophet: str) -> "CitationNexus":
|
|
58
62
|
match prophet:
|
|
@@ -61,12 +65,19 @@ class CitationNexus:
|
|
|
61
65
|
# "freeze" these items
|
|
62
66
|
# setattr(self.cffDASHversion, 'type', Final[str])
|
|
63
67
|
# setattr(self.doi, 'type', Final[str])
|
|
64
|
-
# cffDASHversion: str
|
|
65
|
-
# message: str
|
|
66
|
-
# abstract: str | None = None
|
|
67
|
-
# doi: str | None = None
|
|
68
|
-
# preferredDASHcitation: str | None = None
|
|
69
|
-
# type: str | None = None
|
|
68
|
+
# cffDASHversion: str
|
|
69
|
+
# message: str
|
|
70
|
+
# abstract: str | None = None
|
|
71
|
+
# doi: str | None = None
|
|
72
|
+
# preferredDASHcitation: str | None = None
|
|
73
|
+
# type: str | None = None
|
|
74
|
+
case "GitHub":
|
|
75
|
+
pass
|
|
76
|
+
# "freeze" these items
|
|
77
|
+
# setattr(self.commit, 'type', Final[str])
|
|
78
|
+
# setattr(self.dateDASHreleased, 'type', Final[str])
|
|
79
|
+
# setattr(self.identifiers, 'type', Final[list[str]])
|
|
80
|
+
# setattr(self.repositoryDASHcode, 'type', Final[str])
|
|
70
81
|
case "PyPA":
|
|
71
82
|
pass
|
|
72
83
|
# "freeze" these items
|
|
@@ -76,6 +87,10 @@ class CitationNexus:
|
|
|
76
87
|
# setattr(self.repository, 'type', Final[str])
|
|
77
88
|
# setattr(self.url, 'type', Final[str])
|
|
78
89
|
# setattr(self.version, 'type', Final[str])
|
|
90
|
+
case "PyPI":
|
|
91
|
+
pass
|
|
92
|
+
# "freeze" these items
|
|
93
|
+
# setattr(self.repositoryDASHartifact, 'type', Final[str])
|
|
79
94
|
case "pyprojectDOTtoml":
|
|
80
95
|
pass
|
|
81
96
|
# "freeze" these items
|
|
@@ -84,53 +99,6 @@ class CitationNexus:
|
|
|
84
99
|
# setattr(self.title, 'type', Final[str])
|
|
85
100
|
return self
|
|
86
101
|
|
|
87
|
-
def getNexusCitation(pathFilenameCitationSSOT: pathlib.Path) -> CitationNexus:
|
|
88
|
-
|
|
89
|
-
# `cffconvert.cli.create_citation.create_citation()` is PAINFULLY mundane, but a major problem
|
|
90
|
-
# in the CFF ecosystem is divergence. Therefore, I will use this function so that my code
|
|
91
|
-
# converges with the CFF ecosystem.
|
|
92
|
-
citationObject: cffconvert.Citation = create_citation(infile=pathFilenameCitationSSOT, url=None)
|
|
93
|
-
# `._parse()` is a yaml loader: use it for convergence
|
|
94
|
-
cffobj: Dict[Any, Any] = citationObject._parse()
|
|
95
|
-
|
|
96
|
-
nexusCitation = CitationNexus(
|
|
97
|
-
cffDASHversion=cffobj["cff-version"],
|
|
98
|
-
message=cffobj["message"],
|
|
99
|
-
)
|
|
100
|
-
|
|
101
|
-
Z0Z_list: List[attrs.Attribute] = list(attrs.fields(type(nexusCitation)))
|
|
102
|
-
for Z0Z_field in Z0Z_list:
|
|
103
|
-
cffobjKeyName: str = Z0Z_field.name.replace("DASH", "-")
|
|
104
|
-
cffobjValue = cffobj.get(cffobjKeyName)
|
|
105
|
-
if cffobjValue: # An empty list will be False
|
|
106
|
-
setattr(nexusCitation, Z0Z_field.name, cffobjValue)
|
|
107
|
-
|
|
108
|
-
nexusCitation = nexusCitation.setInStone("Citation")
|
|
109
|
-
return nexusCitation
|
|
110
|
-
|
|
111
|
-
def getPypaMetadata(packageData: Dict[str, Any]) -> PyPAMetadata:
|
|
112
|
-
"""
|
|
113
|
-
Create a PyPA metadata object (version 2.4) from packageData.
|
|
114
|
-
https://packaging.python.org/en/latest/specifications/core-metadata/
|
|
115
|
-
"""
|
|
116
|
-
dictionaryProjectURLs: Dict[str, str] = {}
|
|
117
|
-
for urlName, url in packageData.get("urls", {}).items():
|
|
118
|
-
urlName = urlName.lower()
|
|
119
|
-
if urlName in listProjectURLsTarget:
|
|
120
|
-
dictionaryProjectURLs[urlName] = url
|
|
121
|
-
|
|
122
|
-
metadataRaw = packaging.metadata.RawMetadata(
|
|
123
|
-
keywords=packageData.get("keywords", []),
|
|
124
|
-
license_expression=packageData.get("license", {}).get("text", ""),
|
|
125
|
-
metadata_version="2.4",
|
|
126
|
-
name=packaging.utils.canonicalize_name(packageData.get("name", None), validate=True), # packaging.metadata.InvalidMetadata: 'name' is a required field
|
|
127
|
-
project_urls=dictionaryProjectURLs,
|
|
128
|
-
version=packageData.get("version", None),
|
|
129
|
-
)
|
|
130
|
-
|
|
131
|
-
metadata = PyPAMetadata().from_raw(metadataRaw)
|
|
132
|
-
return metadata
|
|
133
|
-
|
|
134
102
|
def addPypaMetadata(nexusCitation: CitationNexus, metadata: PyPAMetadata) -> CitationNexus:
|
|
135
103
|
if not metadata.name:
|
|
136
104
|
raise ValueError("Metadata name is required.")
|
|
@@ -179,6 +147,146 @@ def add_pyprojectDOTtoml(nexusCitation: CitationNexus, packageData: Dict[str, An
|
|
|
179
147
|
nexusCitation = nexusCitation.setInStone("pyprojectDOTtoml")
|
|
180
148
|
return nexusCitation
|
|
181
149
|
|
|
150
|
+
def getGitHubRelease(nexusCitation: CitationNexus) -> Dict[str, Any]:
|
|
151
|
+
"""Return a dictionary with GitHub release data.
|
|
152
|
+
|
|
153
|
+
The dictionary contains the following keys:
|
|
154
|
+
commit: The commit hash (using the API field 'target_commitish').
|
|
155
|
+
date-released: The published date (in YYYY-MM-DD format).
|
|
156
|
+
identifiers: A list with one identifier object, whose description is
|
|
157
|
+
'The URL for {nexusCitation.title} {nexusCitation.version}.'
|
|
158
|
+
repository-code: A URL for the commit in the repository.
|
|
159
|
+
|
|
160
|
+
Raises:
|
|
161
|
+
ValueError: If the nexusCitation.repository is not set or cannot be parsed.
|
|
162
|
+
RuntimeError: If the HTTP request to GitHub fails.
|
|
163
|
+
"""
|
|
164
|
+
if not nexusCitation.repository:
|
|
165
|
+
raise ValueError("Repository URL is required to get GitHub release info.")
|
|
166
|
+
|
|
167
|
+
urlparts = nexusCitation.repository.replace("https://github.com", "", 1).strip("/").split("/") + [None] * 5
|
|
168
|
+
ownername, reponame, _2, refvalue, *_filename_parts = urlparts
|
|
169
|
+
reponame = reponame.replace(".git", "") # type: ignore # Remove .git from the repository name, if present.
|
|
170
|
+
assert ownername is not None, "URL should include the name of the owner/organization."
|
|
171
|
+
assert reponame is not None, "URL should include the name of the repository."
|
|
172
|
+
if refvalue is None:
|
|
173
|
+
repos_api = f"https://api.github.com/repos/{ownername}/{reponame}/releases/latest"
|
|
174
|
+
headers = GITHUB_API_VERSION_HEADER
|
|
175
|
+
headers.update({"Accept": "application/vnd.github+json"})
|
|
176
|
+
token = os.environ.get("GITHUB_TOKEN")
|
|
177
|
+
headers.update({"Authorization": f"Bearer { token }"})
|
|
178
|
+
response = requests.get(repos_api, headers=headers)
|
|
179
|
+
if response.status_code != 200:
|
|
180
|
+
raise RuntimeError(f"Failed to get GitHub release info: {response.status_code}")
|
|
181
|
+
|
|
182
|
+
releaseData = response.json()
|
|
183
|
+
# commitHash = releaseData.get("target_commitish")
|
|
184
|
+
publishedAt = releaseData.get("published_at")
|
|
185
|
+
if publishedAt:
|
|
186
|
+
# Convert ISO timestamp (e.g., "2020-12-31T12:34:56Z") to "YYYY-MM-DD".
|
|
187
|
+
publishedAt = publishedAt.split("T")[0]
|
|
188
|
+
|
|
189
|
+
releaseHtmlUrl = releaseData.get("html_url")
|
|
190
|
+
identifierDescription = f"The URL for {nexusCitation.title} {nexusCitation.version}."
|
|
191
|
+
return {
|
|
192
|
+
# "commit": commitHash,
|
|
193
|
+
"dateDASHreleased": publishedAt,
|
|
194
|
+
"identifiers": [{
|
|
195
|
+
"type": "url",
|
|
196
|
+
"value": releaseHtmlUrl,
|
|
197
|
+
"description": identifierDescription,
|
|
198
|
+
}],
|
|
199
|
+
"repositoryDASHcode": releaseHtmlUrl,
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
def addGitHubRelease(nexusCitation: CitationNexus) -> CitationNexus:
|
|
203
|
+
"""
|
|
204
|
+
Update the nexusCitation with GitHub release information.
|
|
205
|
+
|
|
206
|
+
This function populates the following fields on the nexusCitation:
|
|
207
|
+
- commit: using the commit hash from GitHub.
|
|
208
|
+
- dateDASHreleased: the release date.
|
|
209
|
+
- identifiers: appends a GitHub-specific identifier.
|
|
210
|
+
- repositoryDASHcode: the URL to view the commit in the repository.
|
|
211
|
+
|
|
212
|
+
Returns:
|
|
213
|
+
The updated CitationNexus instance.
|
|
214
|
+
|
|
215
|
+
Raises:
|
|
216
|
+
Any exception raised by getGitHubRelease.
|
|
217
|
+
"""
|
|
218
|
+
gitHubReleaseData = getGitHubRelease(nexusCitation)
|
|
219
|
+
nexusCitation.commit = gitHubReleaseData.get("commit")
|
|
220
|
+
nexusCitation.dateDASHreleased = gitHubReleaseData.get("dateDASHreleased")
|
|
221
|
+
# Overwrite the existing list of identifiers. This could be better
|
|
222
|
+
nexusCitation.identifiers = gitHubReleaseData.get("identifiers", [])
|
|
223
|
+
nexusCitation.repositoryDASHcode = gitHubReleaseData.get("repositoryDASHcode")
|
|
224
|
+
return nexusCitation
|
|
225
|
+
|
|
226
|
+
def getPyPIrelease(nexusCitation: CitationNexus) -> Dict[str, Any]:
|
|
227
|
+
if not nexusCitation.title:
|
|
228
|
+
raise ValueError("Package name (title) is required to get PyPI release info.")
|
|
229
|
+
if not nexusCitation.version:
|
|
230
|
+
raise ValueError("Package version is required to get PyPI release info.")
|
|
231
|
+
|
|
232
|
+
packageName = packaging.utils.canonicalize_name(nexusCitation.title)
|
|
233
|
+
version = str(nexusCitation.version)
|
|
234
|
+
return {
|
|
235
|
+
"repositoryDASHartifact": f"https://pypi.org/project/{packageName}/{version}/"
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
def addPyPIrelease(nexusCitation: CitationNexus) -> CitationNexus:
|
|
239
|
+
pypiReleaseData = getPyPIrelease(nexusCitation)
|
|
240
|
+
nexusCitation.repositoryDASHartifact = pypiReleaseData.get("repositoryDASHartifact")
|
|
241
|
+
return nexusCitation
|
|
242
|
+
|
|
243
|
+
def getNexusCitation(pathFilenameCitationSSOT: pathlib.Path) -> CitationNexus:
|
|
244
|
+
|
|
245
|
+
# `cffconvert.cli.create_citation.create_citation()` is PAINFULLY mundane, but a major problem
|
|
246
|
+
# in the CFF ecosystem is divergence. Therefore, I will use this function so that my code
|
|
247
|
+
# converges with the CFF ecosystem.
|
|
248
|
+
citationObject: cffconvert.Citation = create_citation(infile=pathFilenameCitationSSOT, url=None)
|
|
249
|
+
# `._parse()` is a yaml loader: use it for convergence
|
|
250
|
+
cffobj: Dict[Any, Any] = citationObject._parse()
|
|
251
|
+
|
|
252
|
+
nexusCitation = CitationNexus(
|
|
253
|
+
cffDASHversion=cffobj["cff-version"],
|
|
254
|
+
message=cffobj["message"],
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
Z0Z_list: List[attrs.Attribute] = list(attrs.fields(type(nexusCitation)))
|
|
258
|
+
for Z0Z_field in Z0Z_list:
|
|
259
|
+
cffobjKeyName: str = Z0Z_field.name.replace("DASH", "-")
|
|
260
|
+
cffobjValue = cffobj.get(cffobjKeyName)
|
|
261
|
+
if cffobjValue: # An empty list will be False
|
|
262
|
+
setattr(nexusCitation, Z0Z_field.name, cffobjValue)
|
|
263
|
+
|
|
264
|
+
nexusCitation = nexusCitation.setInStone("Citation")
|
|
265
|
+
return nexusCitation
|
|
266
|
+
|
|
267
|
+
def getPypaMetadata(packageData: Dict[str, Any]) -> PyPAMetadata:
|
|
268
|
+
"""
|
|
269
|
+
Create a PyPA metadata object (version 2.4) from packageData.
|
|
270
|
+
https://packaging.python.org/en/latest/specifications/core-metadata/
|
|
271
|
+
"""
|
|
272
|
+
dictionaryProjectURLs: Dict[str, str] = {}
|
|
273
|
+
for urlName, url in packageData.get("urls", {}).items():
|
|
274
|
+
urlName = urlName.lower()
|
|
275
|
+
if urlName in listProjectURLsTarget:
|
|
276
|
+
dictionaryProjectURLs[urlName] = url
|
|
277
|
+
|
|
278
|
+
metadataRaw = packaging.metadata.RawMetadata(
|
|
279
|
+
keywords=packageData.get("keywords", []),
|
|
280
|
+
license_expression=packageData.get("license", {}).get("text", ""),
|
|
281
|
+
metadata_version="2.4",
|
|
282
|
+
name=packaging.utils.canonicalize_name(packageData.get("name", None), validate=True), # packaging.metadata.InvalidMetadata: 'name' is a required field
|
|
283
|
+
project_urls=dictionaryProjectURLs,
|
|
284
|
+
version=packageData.get("version", None),
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
metadata = PyPAMetadata().from_raw(metadataRaw)
|
|
288
|
+
return metadata
|
|
289
|
+
|
|
182
290
|
def writeCitation(nexusCitation: CitationNexus, pathFilenameCitationSSOT: pathlib.Path, pathFilenameCitationDOTcffRepo: pathlib.Path):
|
|
183
291
|
# NOTE embarrassingly hacky process to follow
|
|
184
292
|
parameterIndent= 2
|
|
@@ -195,7 +303,7 @@ def writeCitation(nexusCitation: CitationNexus, pathFilenameCitationSSOT: pathli
|
|
|
195
303
|
for keyName in list(dictionaryCitation.keys()):
|
|
196
304
|
dictionaryCitation[keyName.replace("DASH", "-")] = dictionaryCitation.pop(keyName)
|
|
197
305
|
|
|
198
|
-
pathFilenameForValidation =
|
|
306
|
+
pathFilenameForValidation = pathFilenameCitationSSOT.with_stem('validation')
|
|
199
307
|
|
|
200
308
|
def writeStream(pathFilename):
|
|
201
309
|
with open(pathFilename, 'w') as pathlibIsAStealthContextManagerThatRuamelCannotDetectAndRefusesToWorkWith:
|
|
@@ -204,19 +312,23 @@ def writeCitation(nexusCitation: CitationNexus, pathFilenameCitationSSOT: pathli
|
|
|
204
312
|
writeStream(pathFilenameForValidation)
|
|
205
313
|
|
|
206
314
|
citationObject: cffconvert.Citation = create_citation(infile=pathFilenameForValidation, url=None)
|
|
207
|
-
if citationObject.validate(
|
|
315
|
+
if citationObject.validate() is None:
|
|
208
316
|
writeStream(pathFilenameCitationSSOT)
|
|
209
317
|
writeStream(pathFilenameCitationDOTcffRepo)
|
|
210
318
|
|
|
319
|
+
pathFilenameForValidation.unlink()
|
|
320
|
+
|
|
211
321
|
def logistics():
|
|
212
322
|
# Prefer reliable, dynamic values over hardcoded ones
|
|
213
|
-
packageNameHARDCODED: str = 'mapFolding'
|
|
214
|
-
|
|
215
|
-
packageName: str = packageNameHARDCODED
|
|
216
323
|
pathRepoRoot = pathlib.Path(__file__).parent.parent.parent
|
|
217
324
|
pathFilenamePackageSSOT = pathRepoRoot / 'pyproject.toml'
|
|
218
|
-
|
|
219
|
-
|
|
325
|
+
|
|
326
|
+
tomlPackageData: Dict[str, Any] = tomli.loads(pathFilenamePackageSSOT.read_text())['project']
|
|
327
|
+
# https://packaging.python.org/en/latest/specifications/pyproject-toml/
|
|
328
|
+
|
|
329
|
+
packageName: str = tomlPackageData.get("name", None)
|
|
330
|
+
if not packageName:
|
|
331
|
+
raise ValueError("Package name is required.")
|
|
220
332
|
|
|
221
333
|
filenameCitationDOTcff = 'CITATION.cff'
|
|
222
334
|
pathCitations = pathRepoRoot / packageName / 'citations'
|
|
@@ -225,13 +337,17 @@ def logistics():
|
|
|
225
337
|
|
|
226
338
|
nexusCitation = getNexusCitation(pathFilenameCitationSSOT)
|
|
227
339
|
|
|
228
|
-
tomlPackageData: Dict[str, Any] = tomli.loads(pathFilenamePackageSSOT.read_text())['project']
|
|
229
|
-
# https://packaging.python.org/en/latest/specifications/pyproject-toml/
|
|
230
340
|
pypaMetadata: PyPAMetadata = getPypaMetadata(tomlPackageData)
|
|
231
341
|
|
|
232
342
|
nexusCitation = addPypaMetadata(nexusCitation, pypaMetadata)
|
|
233
343
|
nexusCitation = add_pyprojectDOTtoml(nexusCitation, tomlPackageData)
|
|
234
344
|
|
|
345
|
+
nexusCitation = addGitHubRelease(nexusCitation)
|
|
346
|
+
nexusCitation = addPyPIrelease(nexusCitation)
|
|
347
|
+
|
|
348
|
+
filenameGitHubAction = 'updateCitation.yml'
|
|
349
|
+
pathFilenameGitHubAction = pathRepoRoot / '.github' / 'workflows' / filenameGitHubAction
|
|
350
|
+
|
|
235
351
|
writeCitation(nexusCitation, pathFilenameCitationSSOT, pathFilenameCitationDOTcffRepo)
|
|
236
352
|
|
|
237
353
|
if __name__ == '__main__':
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
benchmarks/benchmarking.py,sha256=HD_0NSvuabblg94ftDre6LFnXShTe8MYj3hIodW-zV0,3076
|
|
2
|
+
citations/constants.py,sha256=1n3AC_18LOjmMLJWDo8YOGEIvDY7oZyLtI4QwqdB6z4,73
|
|
3
|
+
citations/updateCitation.py,sha256=ihZFafY1TtVgQ1cnVNAHGCJMgoBUR8cm1qvGobwjNKA,14949
|
|
4
|
+
reference/flattened.py,sha256=6blZ2Y9G8mu1F3gV8SKndPE398t2VVFlsgKlyeJ765A,16538
|
|
5
|
+
reference/hunterNumba.py,sha256=HWndRgsajOf76rbb2LDNEZ6itsdYbyV-k3wgOFjeR6c,7104
|
|
6
|
+
reference/irvineJavaPort.py,sha256=Sj-63Z-OsGuDoEBXuxyjRrNmmyl0d7Yz_XuY7I47Oyg,4250
|
|
7
|
+
reference/jax.py,sha256=rojyK80lOATtbzxjGOHWHZngQa47CXCLJHZwIdN2MwI,14955
|
|
8
|
+
reference/lunnan.py,sha256=XEcql_gxvCCghb6Or3qwmPbn4IZUbZTaSmw_fUjRxZE,5037
|
|
9
|
+
reference/lunnanNumpy.py,sha256=HqDgSwTOZA-G0oophOEfc4zs25Mv4yw2aoF1v8miOLk,4653
|
|
10
|
+
reference/lunnanWhile.py,sha256=7NY2IKO5XBgol0aWWF_Fi-7oTL9pvu_z6lB0TF1uVHk,4063
|
|
11
|
+
reference/rotatedEntryPoint.py,sha256=z0QyDQtnMvXNj5ntWzzJUQUMFm1-xHGLVhtYzwmczUI,11530
|
|
12
|
+
reference/total_countPlus1vsPlusN.py,sha256=usenM8Yn_G1dqlPl7NKKkcnbohBZVZBXTQRm2S3_EDA,8106
|
|
13
|
+
someAssemblyRequired/__init__.py,sha256=3JnAKXfaYPtmxV_4AnZ6KpCosT_0GFV5Nw7K8sz4-Uo,34
|
|
14
|
+
someAssemblyRequired/getLLVMforNoReason.py,sha256=FtJzw2pZS3A4NimWdZsegXaU-vKeCw8m67kcfb5wvGM,894
|
|
15
|
+
someAssemblyRequired/makeJob.py,sha256=UUCNdkFnSrlhRAjkQp2_Qv4joKGXRnNJhZ4OdxI_aqU,2628
|
|
16
|
+
someAssemblyRequired/synthesizeModuleJobNumba.py,sha256=TCYNNI19vCw0C1FLP691VPpYknkedj-zpx-xQFVIVJU,9851
|
|
17
|
+
someAssemblyRequired/synthesizeModulesNumba.py,sha256=aOst0EC5zStLbUSDTpyMoyQe_8_3CLX7JSdHdPmlAsY,22847
|
|
18
|
+
syntheticModules/__init__.py,sha256=XMjNt8x24M82i1eCNVcWpIIhXwfVCnjfbb-y36RAafA,131
|
|
19
|
+
syntheticModules/numbaInitialize.py,sha256=H8Y22wwE5kYyroztAJ5HJ2W6qfw7Fu4iVrUsCPmnFjY,4088
|
|
20
|
+
syntheticModules/numbaParallel.py,sha256=fvprhBepBxQkY5yQInYRWb7agDVMArypGC6Y510tAcU,5452
|
|
21
|
+
syntheticModules/numbaSequential.py,sha256=Knru1I7BBvsBDTex7NfxO2U3IEcQfz-uG4Lpw2tLhOI,3629
|
|
22
|
+
mapFolding-0.3.8.dist-info/METADATA,sha256=hgOrdxAEB5vsBdIK41JvUc65iHQladkMBbbXwg-YPnc,7729
|
|
23
|
+
mapFolding-0.3.8.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
24
|
+
mapFolding-0.3.8.dist-info/entry_points.txt,sha256=F3OUeZR1XDTpoH7k3wXuRb3KF_kXTTeYhu5AGK1SiOQ,146
|
|
25
|
+
mapFolding-0.3.8.dist-info/top_level.txt,sha256=yVG9dNZywoaddcsUdEDg7o0XOBzJd_4Z-sDaXGHpiMY,69
|
|
26
|
+
mapFolding-0.3.8.dist-info/RECORD,,
|
someAssemblyRequired/makeJob.py
CHANGED
|
@@ -1,25 +1,53 @@
|
|
|
1
|
-
|
|
2
|
-
from mapFolding import outfitCountFolds
|
|
1
|
+
import importlib.util
|
|
2
|
+
from mapFolding import getPathFilenameFoldsTotal, computationState, outfitCountFolds, getAlgorithmSource
|
|
3
3
|
from typing import Any, Literal, Optional, Sequence, Type, overload
|
|
4
4
|
import pathlib
|
|
5
5
|
import pickle
|
|
6
6
|
|
|
7
7
|
@overload
|
|
8
8
|
def makeStateJob(listDimensions: Sequence[int], writeJob: Literal[True] = True
|
|
9
|
-
, **keywordArguments: Optional[
|
|
9
|
+
, **keywordArguments: Optional[str]) -> pathlib.Path:
|
|
10
10
|
...
|
|
11
11
|
|
|
12
12
|
@overload
|
|
13
13
|
def makeStateJob(listDimensions: Sequence[int], writeJob: Literal[False] = False
|
|
14
|
-
, **keywordArguments: Optional[
|
|
14
|
+
, **keywordArguments: Optional[str]) -> computationState:
|
|
15
15
|
...
|
|
16
16
|
|
|
17
|
-
def makeStateJob(listDimensions: Sequence[int], writeJob: bool = True, **keywordArguments: Optional[
|
|
17
|
+
def makeStateJob(listDimensions: Sequence[int], writeJob: bool = True, **keywordArguments: Optional[str]) -> computationState | pathlib.Path:
|
|
18
|
+
"""
|
|
19
|
+
Creates a computation state job for map folding calculations and optionally saves it to disk.
|
|
20
|
+
|
|
21
|
+
This function initializes a computation state for map folding calculations based on the given dimensions,
|
|
22
|
+
sets up the initial counting configuration, and can optionally save the state to a pickle file.
|
|
23
|
+
|
|
24
|
+
Parameters
|
|
25
|
+
----------
|
|
26
|
+
listDimensions : Sequence[int]
|
|
27
|
+
The dimensions of the map to be folded, typically as [height, width].
|
|
28
|
+
writeJob : bool, optional
|
|
29
|
+
If True, saves the computation state to disk. If False, returns the state object directly.
|
|
30
|
+
Default is True.
|
|
31
|
+
**keywordArguments : Optional[str]
|
|
32
|
+
Additional keyword arguments to be passed to the outfitCountFolds function.
|
|
33
|
+
|
|
34
|
+
Returns
|
|
35
|
+
-------
|
|
36
|
+
Union[computationState, pathlib.Path]
|
|
37
|
+
If writeJob is False, returns the computation state object.
|
|
38
|
+
If writeJob is True, returns the Path object pointing to the saved state file.
|
|
39
|
+
|
|
40
|
+
Notes
|
|
41
|
+
-----
|
|
42
|
+
The function creates necessary directories and saves the state as a pickle file
|
|
43
|
+
when writeJob is True. The file is saved in a directory structure based on the map shape.
|
|
44
|
+
"""
|
|
18
45
|
|
|
19
46
|
stateUniversal: computationState = outfitCountFolds(listDimensions, computationDivisions=None, CPUlimit=None, **keywordArguments)
|
|
20
47
|
|
|
21
|
-
|
|
22
|
-
|
|
48
|
+
|
|
49
|
+
moduleSource = getAlgorithmSource()
|
|
50
|
+
moduleSource.countInitialize(stateUniversal['connectionGraph'], stateUniversal['gapsWhere'], stateUniversal['my'], stateUniversal['track'])
|
|
23
51
|
|
|
24
52
|
if not writeJob:
|
|
25
53
|
return stateUniversal
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from mapFolding import getPathFilenameFoldsTotal, indexMy, indexTrack
|
|
2
|
-
from mapFolding import
|
|
2
|
+
from mapFolding import setDatatypeElephino, setDatatypeFoldsTotal, setDatatypeLeavesTotal, setDatatypeModule, hackSSOTdatatype
|
|
3
3
|
from someAssemblyRequired import makeStateJob
|
|
4
4
|
from typing import Optional
|
|
5
5
|
import importlib
|
|
@@ -13,9 +13,36 @@ import python_minifier
|
|
|
13
13
|
identifierCallableLaunch = "goGoGadgetAbsurdity"
|
|
14
14
|
|
|
15
15
|
def makeStrRLEcompacted(arrayTarget: numpy.ndarray, identifierName: str) -> str:
|
|
16
|
-
|
|
16
|
+
"""Converts a NumPy array into a compressed string representation using run-length encoding (RLE).
|
|
17
|
+
|
|
18
|
+
This function takes a NumPy array and converts it into an optimized string representation by:
|
|
19
|
+
1. Compressing consecutive sequences of numbers into range objects
|
|
20
|
+
2. Minimizing repeated zeros using array multiplication syntax
|
|
21
|
+
3. Converting the result into a valid Python array initialization statement
|
|
22
|
+
|
|
23
|
+
Parameters:
|
|
24
|
+
arrayTarget (numpy.ndarray): The input NumPy array to be converted
|
|
25
|
+
identifierName (str): The variable name to use in the output string
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
str: A string containing Python code that recreates the input array in compressed form.
|
|
29
|
+
Format: "{identifierName} = numpy.array({compressed_data}, dtype=numpy.{dtype})"
|
|
30
|
+
|
|
31
|
+
Example:
|
|
32
|
+
>>> arr = numpy.array([[0,0,0,1,2,3,4,0,0]])
|
|
33
|
+
>>> print(makeStrRLEcompacted(arr, "myArray"))
|
|
34
|
+
"myArray = numpy.array([[0]*3,*range(1,5),[0]*2], dtype=numpy.int64)"
|
|
35
|
+
|
|
36
|
+
Notes:
|
|
37
|
+
- Sequences of 4 or fewer numbers are kept as individual values
|
|
38
|
+
- Sequences longer than 4 numbers are converted to range objects
|
|
39
|
+
- Consecutive zeros are compressed using multiplication syntax
|
|
40
|
+
- The function preserves the original array's dtype
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
def compressRangesNDArrayNoFlatten(arraySlice):
|
|
17
44
|
if isinstance(arraySlice, numpy.ndarray) and arraySlice.ndim > 1:
|
|
18
|
-
return [
|
|
45
|
+
return [compressRangesNDArrayNoFlatten(arraySlice[index]) for index in range(arraySlice.shape[0])]
|
|
19
46
|
elif isinstance(arraySlice, numpy.ndarray) and arraySlice.ndim == 1:
|
|
20
47
|
listWithRanges = []
|
|
21
48
|
for group in more_itertools.consecutive_groups(arraySlice.tolist()):
|
|
@@ -28,7 +55,7 @@ def makeStrRLEcompacted(arrayTarget: numpy.ndarray, identifierName: str) -> str:
|
|
|
28
55
|
return listWithRanges
|
|
29
56
|
return arraySlice
|
|
30
57
|
|
|
31
|
-
arrayAsNestedLists =
|
|
58
|
+
arrayAsNestedLists = compressRangesNDArrayNoFlatten(arrayTarget)
|
|
32
59
|
|
|
33
60
|
stringMinimized = python_minifier.minify(str(arrayAsNestedLists))
|
|
34
61
|
commaZeroMaximum = arrayTarget.shape[-1] - 1
|
|
@@ -40,25 +67,47 @@ def makeStrRLEcompacted(arrayTarget: numpy.ndarray, identifierName: str) -> str:
|
|
|
40
67
|
|
|
41
68
|
return f"{identifierName} = numpy.array({stringMinimized}, dtype=numpy.{arrayTarget.dtype})"
|
|
42
69
|
|
|
43
|
-
def writeModuleWithNumba(listDimensions
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
70
|
+
def writeModuleWithNumba(listDimensions) -> pathlib.Path:
|
|
71
|
+
"""
|
|
72
|
+
Writes a Numba-optimized Python module for map folding calculations.
|
|
73
|
+
|
|
74
|
+
This function takes map dimensions and generates a specialized Python module with Numba
|
|
75
|
+
optimizations. It processes a sequential counting algorithm, adds Numba decorators and
|
|
76
|
+
necessary data structures, and writes the resulting code to a file.
|
|
77
|
+
|
|
78
|
+
Parameters:
|
|
79
|
+
listDimensions: List of integers representing the dimensions of the map to be folded.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
pathlib.Path: Path to the generated Python module file.
|
|
83
|
+
|
|
84
|
+
The generated module includes:
|
|
85
|
+
- Numba JIT compilation decorators for performance optimization
|
|
86
|
+
- Required numpy and numba imports
|
|
87
|
+
- Dynamic and static data structures needed for folding calculations
|
|
88
|
+
- Processed algorithm from the original sequential counter
|
|
89
|
+
- Launch code for standalone execution
|
|
90
|
+
- Code to write the final fold count to a file
|
|
91
|
+
The function handles:
|
|
92
|
+
- Translation of original code to Numba-compatible syntax
|
|
93
|
+
- Insertion of pre-calculated values from the state job
|
|
94
|
+
- Management of variable declarations and assignments
|
|
95
|
+
- Setup of proper data types for Numba optimization
|
|
96
|
+
- Organization of the output file structure
|
|
97
|
+
|
|
98
|
+
Note:
|
|
99
|
+
The generated module requires Numba and numpy to be installed.
|
|
100
|
+
The output file will be placed in the same directory as the folds total file,
|
|
101
|
+
with a .py extension.
|
|
102
|
+
"""
|
|
103
|
+
stateJob = makeStateJob(listDimensions, writeJob=False)
|
|
54
104
|
pathFilenameFoldsTotal = getPathFilenameFoldsTotal(stateJob['mapShape'])
|
|
55
105
|
|
|
56
106
|
from syntheticModules import countSequential
|
|
57
107
|
algorithmSource = countSequential
|
|
58
108
|
codeSource = inspect.getsource(algorithmSource)
|
|
59
109
|
|
|
60
|
-
|
|
61
|
-
lineNumba = f"@numba.jit(numba.types.{datatypeLarge}(), cache=True, nopython=True, fastmath=True, forceinline=True, inline='always', looplift=False, _nrt=True, error_model='numpy', parallel=False, boundscheck=False, no_cfunc_wrapper=True, no_cpython_wrapper=False)"
|
|
110
|
+
lineNumba = f"@numba.jit(numba.types.{hackSSOTdatatype('datatypeFoldsTotal')}(), cache=True, nopython=True, fastmath=True, forceinline=True, inline='always', looplift=False, _nrt=True, error_model='numpy', parallel=False, boundscheck=False, no_cfunc_wrapper=False, no_cpython_wrapper=False)"
|
|
62
111
|
|
|
63
112
|
linesImport = "\n".join([
|
|
64
113
|
"import numpy"
|
|
@@ -68,8 +117,6 @@ def writeModuleWithNumba(listDimensions, **keywordArguments: Optional[str]) -> p
|
|
|
68
117
|
ImaIndent = ' '
|
|
69
118
|
linesDataDynamic = """"""
|
|
70
119
|
linesDataDynamic = "\n".join([linesDataDynamic
|
|
71
|
-
# , ImaIndent + f"foldsTotal = numba.types.{datatypeLarge}(0)"
|
|
72
|
-
# , ImaIndent + makeStrRLEcompacted(stateJob['foldGroups'], 'foldGroups')
|
|
73
120
|
, ImaIndent + makeStrRLEcompacted(stateJob['gapsWhere'], 'gapsWhere')
|
|
74
121
|
])
|
|
75
122
|
|
|
@@ -97,11 +144,18 @@ def writeModuleWithNumba(listDimensions, **keywordArguments: Optional[str]) -> p
|
|
|
97
144
|
elif 'my[indexMy.' in lineSource:
|
|
98
145
|
if 'dimensionsTotal' in lineSource:
|
|
99
146
|
continue
|
|
100
|
-
# leaf1ndex = my[indexMy.leaf1ndex.value]
|
|
147
|
+
# Statements are in the form: leaf1ndex = my[indexMy.leaf1ndex.value]
|
|
101
148
|
identifier, statement = lineSource.split('=')
|
|
102
|
-
lineSource = ImaIndent + identifier.strip() + f"=numba.types.{
|
|
149
|
+
lineSource = ImaIndent + identifier.strip() + f"=numba.types.{hackSSOTdatatype(identifier.strip())}({str(eval(statement.strip()))})"
|
|
150
|
+
elif ': int =' in lineSource or ':int=' in lineSource:
|
|
151
|
+
if 'dimensionsTotal' in lineSource:
|
|
152
|
+
continue
|
|
153
|
+
# Statements are in the form: groupsOfFolds: int = 0
|
|
154
|
+
assignment, statement = lineSource.split('=')
|
|
155
|
+
identifier = assignment.split(':')[0].strip()
|
|
156
|
+
lineSource = ImaIndent + identifier.strip() + f"=numba.types.{hackSSOTdatatype(identifier.strip())}({str(eval(statement.strip()))})"
|
|
103
157
|
elif 'track[indexTrack.' in lineSource:
|
|
104
|
-
# leafAbove = track[indexTrack.leafAbove.value]
|
|
158
|
+
# Statements are in the form: leafAbove = track[indexTrack.leafAbove.value]
|
|
105
159
|
identifier, statement = lineSource.split('=')
|
|
106
160
|
lineSource = ImaIndent + makeStrRLEcompacted(eval(statement.strip()), identifier.strip())
|
|
107
161
|
elif 'foldGroups[-1]' in lineSource:
|
|
@@ -144,11 +198,11 @@ if __name__ == '__main__':
|
|
|
144
198
|
return pathFilenameDestination
|
|
145
199
|
|
|
146
200
|
if __name__ == '__main__':
|
|
147
|
-
listDimensions = [
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
pathFilenameModule = writeModuleWithNumba(listDimensions
|
|
201
|
+
listDimensions = [5,5]
|
|
202
|
+
setDatatypeFoldsTotal('int64', sourGrapes=True)
|
|
203
|
+
setDatatypeElephino('uint8', sourGrapes=True)
|
|
204
|
+
setDatatypeLeavesTotal('int8', sourGrapes=True)
|
|
205
|
+
pathFilenameModule = writeModuleWithNumba(listDimensions)
|
|
152
206
|
|
|
153
207
|
# Induce numba.jit compilation
|
|
154
208
|
moduleSpec = importlib.util.spec_from_file_location(pathFilenameModule.stem, pathFilenameModule)
|