mapFolding 0.8.6__py3-none-any.whl → 0.9.1__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.
- mapFolding/__init__.py +60 -13
- mapFolding/basecamp.py +32 -17
- mapFolding/beDRY.py +4 -5
- mapFolding/oeis.py +94 -7
- mapFolding/someAssemblyRequired/RecipeJob.py +103 -0
- mapFolding/someAssemblyRequired/__init__.py +71 -50
- mapFolding/someAssemblyRequired/_theTypes.py +11 -15
- mapFolding/someAssemblyRequired/_tool_Make.py +36 -9
- mapFolding/someAssemblyRequired/_tool_Then.py +59 -25
- mapFolding/someAssemblyRequired/_toolboxAntecedents.py +159 -272
- mapFolding/someAssemblyRequired/_toolboxContainers.py +155 -70
- mapFolding/someAssemblyRequired/_toolboxPython.py +168 -44
- mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +154 -39
- mapFolding/someAssemblyRequired/toolboxNumba.py +72 -230
- mapFolding/someAssemblyRequired/transformationTools.py +370 -141
- mapFolding/syntheticModules/{numbaCount_doTheNeedful.py → numbaCount.py} +7 -4
- mapFolding/theDao.py +19 -16
- mapFolding/theSSOT.py +165 -62
- mapFolding/toolboxFilesystem.py +1 -1
- mapfolding-0.9.1.dist-info/METADATA +177 -0
- mapfolding-0.9.1.dist-info/RECORD +47 -0
- tests/__init__.py +44 -0
- tests/conftest.py +75 -7
- tests/test_computations.py +92 -10
- tests/test_filesystem.py +32 -33
- tests/test_other.py +0 -1
- tests/test_tasks.py +1 -1
- mapFolding/someAssemblyRequired/newInliner.py +0 -22
- mapfolding-0.8.6.dist-info/METADATA +0 -190
- mapfolding-0.8.6.dist-info/RECORD +0 -47
- {mapfolding-0.8.6.dist-info → mapfolding-0.9.1.dist-info}/WHEEL +0 -0
- {mapfolding-0.8.6.dist-info → mapfolding-0.9.1.dist-info}/entry_points.txt +0 -0
- {mapfolding-0.8.6.dist-info → mapfolding-0.9.1.dist-info}/licenses/LICENSE +0 -0
- {mapfolding-0.8.6.dist-info → mapfolding-0.9.1.dist-info}/top_level.txt +0 -0
|
@@ -1,15 +1,45 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
Job-specific Numba Code Generation for Map Folding Calculations
|
|
3
|
+
|
|
4
|
+
This module specializes in generating highly-optimized, single-purpose Numba modules
|
|
5
|
+
for specific map folding calculation jobs. Unlike the general-purpose transformation
|
|
6
|
+
in toolboxNumba.py, this module creates standalone Python modules optimized for a
|
|
7
|
+
single map shape with statically-encoded parameters.
|
|
8
|
+
|
|
9
|
+
The code generation pipeline focuses on:
|
|
10
|
+
|
|
11
|
+
1. Converting function parameters to initialized variables with concrete values.
|
|
12
|
+
2. Replacing dynamic computations with statically-known values.
|
|
13
|
+
3. Eliminating unused code paths and variables.
|
|
14
|
+
4. Adding progress tracking for long-running calculations.
|
|
15
|
+
5. Applying appropriate Numba optimizations for the specific calculation.
|
|
16
|
+
|
|
17
|
+
This creates extremely fast, specialized implementations that can be run directly
|
|
18
|
+
as Python scripts or further compiled into standalone executables.
|
|
19
|
+
"""
|
|
20
|
+
|
|
2
21
|
from mapFolding.toolboxFilesystem import getPathFilenameFoldsTotal
|
|
3
|
-
from mapFolding.someAssemblyRequired import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
22
|
+
from mapFolding.someAssemblyRequired import (
|
|
23
|
+
ast_Identifier,
|
|
24
|
+
be,
|
|
25
|
+
ifThis,
|
|
26
|
+
IngredientsFunction,
|
|
27
|
+
IngredientsModule,
|
|
28
|
+
LedgerOfImports,
|
|
29
|
+
Make,
|
|
30
|
+
NodeChanger,
|
|
31
|
+
NodeTourist,
|
|
32
|
+
Then,
|
|
33
|
+
)
|
|
34
|
+
from mapFolding.someAssemblyRequired.toolboxNumba import parametersNumbaLight, SpicesJobNumba, decorateCallableWithNumba
|
|
35
|
+
from mapFolding.someAssemblyRequired.transformationTools import extractFunctionDef, write_astModule, makeInitializedComputationState
|
|
36
|
+
from mapFolding.someAssemblyRequired.RecipeJob import RecipeJob
|
|
37
|
+
from mapFolding import The, raiseIfNoneGitHubIssueNumber3, getFoldsTotalKnown
|
|
9
38
|
from typing import cast
|
|
10
39
|
from Z0Z_tools import autoDecodingRLE
|
|
11
40
|
from pathlib import PurePosixPath
|
|
12
41
|
import ast
|
|
42
|
+
"""Synthesize one file to compute `foldsTotal` of `mapShape`."""
|
|
13
43
|
|
|
14
44
|
list_IdentifiersNotUsedAllHARDCODED = ['concurrencyLimit', 'foldsTotal', 'mapShape',]
|
|
15
45
|
list_IdentifiersNotUsedParallelSequentialHARDCODED = ['indexLeaf']
|
|
@@ -22,7 +52,30 @@ list_IdentifiersStaticValuesHARDCODED = ['dimensionsTotal', 'leavesTotal',]
|
|
|
22
52
|
list_IdentifiersNotUsedHARDCODED = list_IdentifiersStaticValuesHARDCODED + list_IdentifiersReplacedHARDCODED + list_IdentifiersNotUsedAllHARDCODED + list_IdentifiersNotUsedParallelSequentialHARDCODED + list_IdentifiersNotUsedSequentialHARDCODED
|
|
23
53
|
|
|
24
54
|
def addLauncherNumbaProgress(ingredientsModule: IngredientsModule, ingredientsFunction: IngredientsFunction, job: RecipeJob, spices: SpicesJobNumba) -> tuple[IngredientsModule, IngredientsFunction]:
|
|
55
|
+
"""
|
|
56
|
+
Add progress tracking capabilities to a Numba-optimized function.
|
|
25
57
|
|
|
58
|
+
This function modifies both the module and the function to integrate Numba-compatible
|
|
59
|
+
progress tracking for long-running calculations. It performs several key transformations:
|
|
60
|
+
|
|
61
|
+
1. Adds a progress bar parameter to the function signature
|
|
62
|
+
2. Replaces counting increments with progress bar updates
|
|
63
|
+
3. Creates a launcher section that displays and updates progress
|
|
64
|
+
4. Configures file output to save results upon completion
|
|
65
|
+
|
|
66
|
+
The progress tracking is particularly important for map folding calculations
|
|
67
|
+
which can take hours or days to complete, providing visual feedback and
|
|
68
|
+
estimated completion times.
|
|
69
|
+
|
|
70
|
+
Parameters:
|
|
71
|
+
ingredientsModule: The module where the function is defined.
|
|
72
|
+
ingredientsFunction: The function to modify with progress tracking.
|
|
73
|
+
job: Configuration specifying shape details and output paths.
|
|
74
|
+
spices: Configuration specifying progress bar details.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
A tuple containing the modified module and function with progress tracking.
|
|
78
|
+
"""
|
|
26
79
|
linesLaunch: str = f"""
|
|
27
80
|
if __name__ == '__main__':
|
|
28
81
|
with ProgressBar(total={job.foldsTotalEstimated}, update_interval=2) as statusUpdate:
|
|
@@ -43,20 +96,51 @@ if __name__ == '__main__':
|
|
|
43
96
|
|
|
44
97
|
findThis = ifThis.isAugAssign_targetIs(ifThis.isName_Identifier(job.shatteredDataclass.countingVariableName.id))
|
|
45
98
|
doThat = Then.replaceWith(Make.Expr(Make.Call(Make.Attribute(Make.Name(spices.numbaProgressBarIdentifier),'update'),[Make.Constant(1)])))
|
|
46
|
-
countWithProgressBar = NodeChanger(findThis, doThat)
|
|
99
|
+
countWithProgressBar = NodeChanger(findThis, doThat)
|
|
47
100
|
countWithProgressBar.visit(ingredientsFunction.astFunctionDef)
|
|
48
101
|
|
|
102
|
+
removeReturnStatement = NodeChanger(be.Return, Then.removeIt)
|
|
103
|
+
removeReturnStatement.visit(ingredientsFunction.astFunctionDef)
|
|
104
|
+
ingredientsFunction.astFunctionDef.returns = Make.Constant(value=None)
|
|
105
|
+
|
|
49
106
|
ingredientsModule.appendLauncher(ast.parse(linesLaunch))
|
|
50
107
|
|
|
51
108
|
return ingredientsModule, ingredientsFunction
|
|
52
109
|
|
|
53
110
|
def move_arg2FunctionDefDOTbodyAndAssignInitialValues(ingredientsFunction: IngredientsFunction, job: RecipeJob) -> IngredientsFunction:
|
|
111
|
+
"""
|
|
112
|
+
Convert function parameters into initialized variables with concrete values.
|
|
113
|
+
|
|
114
|
+
This function implements a critical transformation that converts function parameters
|
|
115
|
+
into statically initialized variables in the function body. This enables several
|
|
116
|
+
optimizations:
|
|
117
|
+
|
|
118
|
+
1. Eliminating parameter passing overhead.
|
|
119
|
+
2. Embedding concrete values directly in the code.
|
|
120
|
+
3. Allowing Numba to optimize based on known value characteristics.
|
|
121
|
+
4. Simplifying function signatures for specialized use cases.
|
|
122
|
+
|
|
123
|
+
The function handles different data types (scalars, arrays, custom types) appropriately,
|
|
124
|
+
replacing abstract parameter references with concrete values from the computation state.
|
|
125
|
+
It also removes unused parameters and variables to eliminate dead code.
|
|
126
|
+
|
|
127
|
+
Parameters:
|
|
128
|
+
ingredientsFunction: The function to transform.
|
|
129
|
+
job: Recipe containing concrete values for parameters and field metadata.
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
The modified function with parameters converted to initialized variables.
|
|
133
|
+
"""
|
|
54
134
|
ingredientsFunction.imports.update(job.shatteredDataclass.ledger)
|
|
55
135
|
|
|
56
|
-
|
|
136
|
+
list_argCuzMyBrainRefusesToThink = ingredientsFunction.astFunctionDef.args.args + ingredientsFunction.astFunctionDef.args.posonlyargs + ingredientsFunction.astFunctionDef.args.kwonlyargs
|
|
137
|
+
list_arg_arg: list[ast_Identifier] = [ast_arg.arg for ast_arg in list_argCuzMyBrainRefusesToThink]
|
|
138
|
+
listName: list[ast.Name] = []
|
|
139
|
+
NodeTourist(be.Name, Then.appendTo(listName)).visit(ingredientsFunction.astFunctionDef)
|
|
140
|
+
list_Identifiers: list[ast_Identifier] = [astName.id for astName in listName]
|
|
141
|
+
list_IdentifiersNotUsed: list[ast_Identifier] = list(set(list_arg_arg) - set(list_Identifiers))
|
|
57
142
|
|
|
58
|
-
|
|
59
|
-
for ast_arg in list_argCauseMyBrainRefusesToDoThisTheRightWay:
|
|
143
|
+
for ast_arg in list_argCuzMyBrainRefusesToThink:
|
|
60
144
|
if ast_arg.arg in job.shatteredDataclass.field2AnnAssign:
|
|
61
145
|
if ast_arg.arg in list_IdentifiersNotUsed:
|
|
62
146
|
pass
|
|
@@ -64,11 +148,11 @@ def move_arg2FunctionDefDOTbodyAndAssignInitialValues(ingredientsFunction: Ingre
|
|
|
64
148
|
ImaAnnAssign, elementConstructor = job.shatteredDataclass.Z0Z_field2AnnAssign[ast_arg.arg]
|
|
65
149
|
match elementConstructor:
|
|
66
150
|
case 'scalar':
|
|
67
|
-
ImaAnnAssign.value.args[0].value = int(job.state.__dict__[ast_arg.arg])
|
|
151
|
+
ImaAnnAssign.value.args[0].value = int(job.state.__dict__[ast_arg.arg]) # type: ignore
|
|
68
152
|
case 'array':
|
|
69
153
|
dataAsStrRLE: str = autoDecodingRLE(job.state.__dict__[ast_arg.arg], addSpaces=True)
|
|
70
154
|
dataAs_astExpr: ast.expr = cast(ast.Expr, ast.parse(dataAsStrRLE).body[0]).value
|
|
71
|
-
ImaAnnAssign.value.args = [dataAs_astExpr]
|
|
155
|
+
ImaAnnAssign.value.args = [dataAs_astExpr] # type: ignore
|
|
72
156
|
case _:
|
|
73
157
|
list_exprDOTannotation: list[ast.expr] = []
|
|
74
158
|
list_exprDOTvalue: list[ast.expr] = []
|
|
@@ -87,17 +171,34 @@ def move_arg2FunctionDefDOTbodyAndAssignInitialValues(ingredientsFunction: Ingre
|
|
|
87
171
|
ast.fix_missing_locations(ingredientsFunction.astFunctionDef)
|
|
88
172
|
return ingredientsFunction
|
|
89
173
|
|
|
90
|
-
def makeJobNumba(job: RecipeJob, spices: SpicesJobNumba):
|
|
91
|
-
|
|
174
|
+
def makeJobNumba(job: RecipeJob, spices: SpicesJobNumba) -> None:
|
|
175
|
+
"""
|
|
176
|
+
Generate a highly-optimized, single-purpose Numba module for a specific map shape.
|
|
177
|
+
|
|
178
|
+
This function implements the complete transformation pipeline for creating a
|
|
179
|
+
standalone, specialized implementation for calculating map folding solutions for
|
|
180
|
+
a specific shape. The process includes:
|
|
181
|
+
|
|
182
|
+
1. Extracting the counting function from the source module
|
|
183
|
+
2. Removing unused code paths based on static analysis
|
|
184
|
+
3. Replacing dynamic variables with concrete values
|
|
185
|
+
4. Converting parameters to initialized variables
|
|
186
|
+
5. Adding progress tracking if requested
|
|
187
|
+
6. Applying Numba optimizations and type specifications
|
|
188
|
+
7. Writing the final module to the filesystem
|
|
189
|
+
|
|
190
|
+
The resulting Python module is both human-readable and extraordinarily efficient,
|
|
191
|
+
with all shape-specific optimizations statically encoded. This creates specialized
|
|
192
|
+
implementations that can be orders of magnitude faster than general-purpose code.
|
|
193
|
+
|
|
194
|
+
Parameters:
|
|
195
|
+
job: Configuration specifying the target shape, paths, and computation state.
|
|
196
|
+
spices: Configuration specifying Numba and progress tracking options.
|
|
197
|
+
"""
|
|
92
198
|
astFunctionDef = extractFunctionDef(job.source_astModule, job.countCallable)
|
|
93
199
|
if not astFunctionDef: raise raiseIfNoneGitHubIssueNumber3
|
|
94
200
|
ingredientsCount: IngredientsFunction = IngredientsFunction(astFunctionDef, LedgerOfImports())
|
|
95
201
|
|
|
96
|
-
# Change the return so you can dynamically determine which variables are not used
|
|
97
|
-
removeReturnStatement = NodeChanger(be.Return, Then.removeIt)
|
|
98
|
-
removeReturnStatement.visit(ingredientsCount.astFunctionDef)
|
|
99
|
-
ingredientsCount.astFunctionDef.returns = Make.Constant(value=None)
|
|
100
|
-
|
|
101
202
|
# Remove `foldGroups` and any other unused statements, so you can dynamically determine which variables are not used
|
|
102
203
|
findThis = ifThis.isAssignAndTargets0Is(ifThis.isSubscript_Identifier('foldGroups'))
|
|
103
204
|
doThat = Then.removeIt
|
|
@@ -109,32 +210,50 @@ def makeJobNumba(job: RecipeJob, spices: SpicesJobNumba):
|
|
|
109
210
|
for identifier in list_IdentifiersStaticValues:
|
|
110
211
|
findThis = ifThis.isName_Identifier(identifier)
|
|
111
212
|
doThat = Then.replaceWith(Make.Constant(int(job.state.__dict__[identifier])))
|
|
112
|
-
NodeChanger(findThis, doThat).visit(ingredientsCount.astFunctionDef)
|
|
213
|
+
NodeChanger(findThis, doThat).visit(ingredientsCount.astFunctionDef)
|
|
113
214
|
|
|
114
|
-
# This launcher eliminates the use of one identifier, so run it now and you can dynamically determine which variables are not used
|
|
115
215
|
ingredientsModule = IngredientsModule()
|
|
216
|
+
# This launcher eliminates the use of one identifier, so run it now and you can dynamically determine which variables are not used
|
|
116
217
|
if spices.useNumbaProgressBar:
|
|
117
218
|
ingredientsModule, ingredientsCount = addLauncherNumbaProgress(ingredientsModule, ingredientsCount, job, spices)
|
|
118
219
|
spices.parametersNumba['nogil'] = True
|
|
220
|
+
else:
|
|
221
|
+
linesLaunch: str = f"""
|
|
222
|
+
if __name__ == '__main__':
|
|
223
|
+
import time
|
|
224
|
+
timeStart = time.perf_counter()
|
|
225
|
+
foldsTotal = {job.countCallable}() * {job.state.leavesTotal}
|
|
226
|
+
print(time.perf_counter() - timeStart)
|
|
227
|
+
print('\\nmap {job.state.mapShape} =', foldsTotal)
|
|
228
|
+
writeStream = open('{job.pathFilenameFoldsTotal.as_posix()}', 'w')
|
|
229
|
+
writeStream.write(str(foldsTotal))
|
|
230
|
+
writeStream.close()
|
|
231
|
+
from mapFolding.oeis import getFoldsTotalKnown
|
|
232
|
+
print(foldsTotal == getFoldsTotalKnown({job.state.mapShape}))
|
|
233
|
+
"""
|
|
234
|
+
ingredientsModule.appendLauncher(ast.parse(linesLaunch))
|
|
235
|
+
changeReturnParallelCallable = NodeChanger(be.Return, Then.replaceWith(Make.Return(job.shatteredDataclass.countingVariableName)))
|
|
236
|
+
changeReturnParallelCallable.visit(ingredientsCount.astFunctionDef)
|
|
237
|
+
ingredientsCount.astFunctionDef.returns = job.shatteredDataclass.countingVariableAnnotation
|
|
119
238
|
|
|
120
239
|
ingredientsCount = move_arg2FunctionDefDOTbodyAndAssignInitialValues(ingredientsCount, job)
|
|
121
240
|
|
|
122
241
|
Z0Z_Identifier = 'DatatypeLeavesTotal'
|
|
123
242
|
Z0Z_type = 'uint8'
|
|
124
243
|
ingredientsModule.imports.addImportFrom_asStr('numba', Z0Z_type)
|
|
125
|
-
|
|
126
|
-
Z0Z_annotation = 'TypeAlias'
|
|
127
|
-
ingredientsModule.imports.addImportFrom_asStr(Z0Z_module, Z0Z_annotation)
|
|
128
|
-
Z0Z_statement = Make.AnnAssign(Make.Name(Z0Z_Identifier, ast.Store()), Make.Name(Z0Z_annotation), Make.Name(Z0Z_type))
|
|
244
|
+
Z0Z_statement = Make.Assign([Make.Name(Z0Z_Identifier, ast.Store())], Make.Name(Z0Z_type))
|
|
129
245
|
ingredientsModule.appendPrologue(statement=Z0Z_statement)
|
|
130
246
|
|
|
131
247
|
Z0Z_Identifier = 'DatatypeElephino'
|
|
132
|
-
Z0Z_type = '
|
|
248
|
+
Z0Z_type = 'uint8'
|
|
133
249
|
ingredientsModule.imports.addImportFrom_asStr('numba', Z0Z_type)
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
250
|
+
Z0Z_statement = Make.Assign([Make.Name(Z0Z_Identifier, ast.Store())], Make.Name(Z0Z_type))
|
|
251
|
+
ingredientsModule.appendPrologue(statement=Z0Z_statement)
|
|
252
|
+
|
|
253
|
+
Z0Z_Identifier = 'DatatypeFoldsTotal'
|
|
254
|
+
Z0Z_type = 'int64'
|
|
255
|
+
ingredientsModule.imports.addImportFrom_asStr('numba', Z0Z_type)
|
|
256
|
+
Z0Z_statement = Make.Assign([Make.Name(Z0Z_Identifier, ast.Store())], Make.Name(Z0Z_type))
|
|
138
257
|
ingredientsModule.appendPrologue(statement=Z0Z_statement)
|
|
139
258
|
|
|
140
259
|
ingredientsCount.imports.removeImportFromModule('mapFolding.theSSOT')
|
|
@@ -145,23 +264,18 @@ def makeJobNumba(job: RecipeJob, spices: SpicesJobNumba):
|
|
|
145
264
|
ingredientsCount.imports.addImportFrom_asStr(Z0Z_module, Z0Z_type_name, Z0Z_asname)
|
|
146
265
|
Z0Z_asname = 'Array1DElephino'
|
|
147
266
|
ingredientsCount.imports.removeImportFrom(Z0Z_module, None, Z0Z_asname)
|
|
148
|
-
Z0Z_type_name = '
|
|
267
|
+
Z0Z_type_name = 'uint8'
|
|
149
268
|
ingredientsCount.imports.addImportFrom_asStr(Z0Z_module, Z0Z_type_name, Z0Z_asname)
|
|
150
269
|
Z0Z_asname = 'Array3D'
|
|
151
270
|
ingredientsCount.imports.removeImportFrom(Z0Z_module, None, Z0Z_asname)
|
|
152
271
|
Z0Z_type_name = 'uint8'
|
|
153
272
|
ingredientsCount.imports.addImportFrom_asStr(Z0Z_module, Z0Z_type_name, Z0Z_asname)
|
|
154
273
|
|
|
155
|
-
from numpy import int16 as Array1DLeavesTotal, int16 as Array1DElephino, int16 as Array3D
|
|
156
|
-
|
|
157
274
|
ingredientsCount.astFunctionDef.decorator_list = [] # TODO low-priority, handle this more elegantly
|
|
158
275
|
# TODO when I add the function signature in numba style back to the decorator, the logic needs to handle `ProgressBarType:`
|
|
159
276
|
ingredientsCount = decorateCallableWithNumba(ingredientsCount, spices.parametersNumba)
|
|
160
277
|
|
|
161
278
|
ingredientsModule.appendIngredientsFunction(ingredientsCount)
|
|
162
|
-
|
|
163
|
-
# add imports, make str, remove unused imports
|
|
164
|
-
# put on disk
|
|
165
279
|
write_astModule(ingredientsModule, job.pathFilenameModule, job.packageIdentifier)
|
|
166
280
|
|
|
167
281
|
"""
|
|
@@ -186,11 +300,12 @@ def makeJobNumba(job: RecipeJob, spices: SpicesJobNumba):
|
|
|
186
300
|
"""
|
|
187
301
|
|
|
188
302
|
if __name__ == '__main__':
|
|
189
|
-
mapShape = (
|
|
303
|
+
mapShape = (1,24)
|
|
190
304
|
state = makeInitializedComputationState(mapShape)
|
|
191
305
|
foldsTotalEstimated = getFoldsTotalKnown(state.mapShape) // state.leavesTotal
|
|
192
306
|
pathModule = PurePosixPath(The.pathPackage, 'jobs')
|
|
193
307
|
pathFilenameFoldsTotal = PurePosixPath(getPathFilenameFoldsTotal(state.mapShape, pathModule))
|
|
194
308
|
aJob = RecipeJob(state, foldsTotalEstimated, pathModule=pathModule, pathFilenameFoldsTotal=pathFilenameFoldsTotal)
|
|
195
|
-
spices = SpicesJobNumba()
|
|
309
|
+
spices = SpicesJobNumba(useNumbaProgressBar=False, parametersNumba=parametersNumbaLight)
|
|
310
|
+
# spices = SpicesJobNumba()
|
|
196
311
|
makeJobNumba(aJob, spices)
|