mapFolding 0.8.1__py3-none-any.whl → 0.8.2__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/theSSOT.py CHANGED
@@ -19,114 +19,85 @@ to avoid namespace collisions when transforming algorithms.
19
19
  from collections.abc import Callable
20
20
  from importlib import import_module as importlib_import_module
21
21
  from inspect import getfile as inspect_getfile
22
- from numpy import dtype, int64 as numpy_int64, int16 as numpy_int16, ndarray, signedinteger
22
+ from numpy import dtype, int64 as numpy_int64, int16 as numpy_int16, ndarray
23
23
  from pathlib import Path
24
24
  from sys import modules as sysModules
25
25
  from tomli import load as tomli_load
26
26
  from types import ModuleType
27
- from typing import Any, Final, TypeAlias
27
+ from typing import TypeAlias
28
28
  import dataclasses
29
29
 
30
- """
31
- 2025 March 11
32
- Note to self: fundamental concept in Python:
33
- Identifiers: scope and resolution, LEGB (Local, Enclosing, Global, Builtin)
34
- - Local: Inside the function
35
- - Enclosing: Inside enclosing functions
36
- - Global: At the uppermost level
37
- - Builtin: Python's built-in names
38
- """
39
-
40
- # I _think_, in theSSOT, I have abstracted the flow settings to only these couple of lines:
41
30
  # Figure out dynamic flow control to synthesized modules https://github.com/hunterhogan/mapFolding/issues/4
31
+ # I _think_, in theSSOT, I have abstracted the flow settings to only these couple of lines:
42
32
  packageFlowSynthetic = 'numba'
43
33
  # Z0Z_packageFlow = 'algorithm'
44
34
  Z0Z_packageFlow = packageFlowSynthetic
45
35
  Z0Z_concurrencyPackage = 'multiprocessing'
46
- # =============================================================================
47
- # The Wrong Way The Wrong Way The Wrong Way The Wrong Way The Wrong Way
48
- # Evaluate When Packaging Evaluate When Packaging Evaluate When Packaging
49
-
50
- sourceAlgorithmPACKAGING: str = 'theDao'
51
- datatypePackagePACKAGING: Final[str] = 'numpy'
52
- dispatcherCallablePACKAGING: str = 'doTheNeedful'
53
- moduleOfSyntheticModulesPACKAGING: Final[str] = 'syntheticModules'
54
36
 
55
- dataclassModulePACKAGING: str = 'theSSOT'
56
- dataclassIdentifierPACKAGING: str = 'ComputationState'
57
- dataclassInstancePACKAGING: str = 'state'
58
- dataclassInstanceTaskDistributionPACKAGING = dataclassInstancePACKAGING + 'Parallel'
59
-
60
- sourceInitializeCallablePACKAGING = 'countInitialize'
61
- sourceSequentialCallablePACKAGING = 'countSequential'
62
- sourceParallelCallablePACKAGING = 'countParallel'
37
+ # =============================================================================
38
+ # The Wrong Way: Evaluate When Packaging
63
39
 
64
40
  try:
65
- thePackageNamePACKAGING: str = tomli_load(Path("../pyproject.toml").open('rb'))["project"]["name"]
41
+ packageNamePACKAGING: str = tomli_load(Path("../pyproject.toml").open('rb'))["project"]["name"]
66
42
  except Exception:
67
- thePackageNamePACKAGING = "mapFolding"
68
-
69
- # =============================================================================
70
- # The Wrong Way The Wrong Way The Wrong Way The Wrong Way The Wrong Way
71
- # Evaluate When Installing Evaluate When Installing Evaluate When Installing
43
+ packageNamePACKAGING = "mapFolding"
72
44
 
73
- fileExtensionINSTALLING: str = '.py'
45
+ # The Wrong Way: Evaluate When Installing
74
46
 
75
47
  def getPathPackageINSTALLING() -> Path:
76
- pathPackage: Path = Path(inspect_getfile(importlib_import_module(thePackageNamePACKAGING)))
48
+ pathPackage: Path = Path(inspect_getfile(importlib_import_module(packageNamePACKAGING)))
77
49
  if pathPackage.is_file():
78
50
  pathPackage = pathPackage.parent
79
51
  return pathPackage
80
52
 
81
- # =============================================================================
82
- # The right way, perhaps.
83
-
84
- # Create enduring identifiers from the hopefully transient identifiers above.
85
- thePackageName: Final[str] = thePackageNamePACKAGING
86
- thePathPackage: Path = getPathPackageINSTALLING()
87
-
88
- """
89
- NOTE on semiotics: `theIdentifier` vs `identifier`
90
-
91
- - This package has a typical, "hardcoded" algorithm for counting map folds.
92
- - This package has logic for transforming that algorithm into other forms.
93
- - The transformation logic can transform other algorithms if 1) they are similar enough to the "hardcoded" algorithm and 2) I have written the transformation logic well enough to handle the differences.
94
- - To avoid confusion and namespace collisions, I differentiate between, for example, `theSourceAlgorithm` of the package and any other `sourceAlgorithm` being transformed by the package.
95
- """
96
-
97
- theSourceAlgorithm: str = sourceAlgorithmPACKAGING
98
- theSourceInitializeCallable = sourceInitializeCallablePACKAGING
99
- theSourceSequentialCallable = sourceSequentialCallablePACKAGING
100
- theSourceParallelCallable = sourceParallelCallablePACKAGING
101
- theDatatypePackage: Final[str] = datatypePackagePACKAGING
102
-
103
- theDispatcherCallable: str = dispatcherCallablePACKAGING
104
-
105
- theDataclassModule: str = dataclassModulePACKAGING
106
- theDataclassIdentifier: str = dataclassIdentifierPACKAGING
107
- theDataclassInstance: str = dataclassInstancePACKAGING
108
- theDataclassInstanceTaskDistribution: str = dataclassInstanceTaskDistributionPACKAGING
109
-
110
- theFileExtension: str = fileExtensionINSTALLING
111
-
112
- theModuleOfSyntheticModules: Final[str] = moduleOfSyntheticModulesPACKAGING
53
+ # The following is an improvement, but it is not the full solution.
54
+ # I hope that the standardized markers, `metadata={'evaluateWhen': 'packaging'}` will help to automate
55
+ # whatever needs to happen so that the following is well implemented.
56
+ @dataclasses.dataclass(frozen=True)
57
+ class PackageSettings:
58
+ concurrencyPackage = Z0Z_concurrencyPackage
59
+ dataclassIdentifier: str = dataclasses.field(default='ComputationState', metadata={'evaluateWhen': 'packaging'})
60
+ dataclassInstance: str = dataclasses.field(default='state', metadata={'evaluateWhen': 'packaging'})
61
+ dataclassInstanceTaskDistributionSuffix: str = dataclasses.field(default='Parallel', metadata={'evaluateWhen': 'packaging'})
62
+ dataclassModule: str = dataclasses.field(default='theSSOT', metadata={'evaluateWhen': 'packaging'})
63
+ datatypePackage: str = dataclasses.field(default='numpy', metadata={'evaluateWhen': 'packaging'})
64
+ dispatcherCallable: str = dataclasses.field(default='doTheNeedful', metadata={'evaluateWhen': 'packaging'})
65
+ fileExtension: str = dataclasses.field(default='.py', metadata={'evaluateWhen': 'installing'})
66
+ moduleOfSyntheticModules: str = dataclasses.field(default='syntheticModules', metadata={'evaluateWhen': 'packaging'})
67
+ packageName: str = dataclasses.field(default = packageNamePACKAGING, metadata={'evaluateWhen': 'packaging'})
68
+ pathPackage: Path = dataclasses.field(default_factory=getPathPackageINSTALLING, init=False, metadata={'evaluateWhen': 'installing'})
69
+ sourceAlgorithm: str = dataclasses.field(default='theDao', metadata={'evaluateWhen': 'packaging'})
70
+ sourceConcurrencyManagerIdentifier: str = dataclasses.field(default='submit', metadata={'evaluateWhen': 'packaging'})
71
+ sourceConcurrencyManagerNamespace: str = dataclasses.field(default='concurrencyManager', metadata={'evaluateWhen': 'packaging'})
72
+ sourceInitializeCallable: str = dataclasses.field(default='countInitialize', metadata={'evaluateWhen': 'packaging'})
73
+ sourceParallelCallable: str = dataclasses.field(default='countParallel', metadata={'evaluateWhen': 'packaging'})
74
+ sourceSequentialCallable: str = dataclasses.field(default='countSequential', metadata={'evaluateWhen': 'packaging'})
75
+
76
+ @property # These are not fields, and that annoys me.
77
+ def dataclassInstanceTaskDistribution(self) -> str:
78
+ """ Compute the task distribution identifier by concatenating dataclassInstance and dataclassInstanceTaskDistributionSuffix. """
79
+ # it follows that `metadata={'evaluateWhen': 'packaging'}`
80
+ return self.dataclassInstance + self.dataclassInstanceTaskDistributionSuffix
81
+
82
+ @property # These are not fields, and that annoys me.
83
+ def logicalPathModuleSourceAlgorithm(self) -> str:
84
+ """ Compute the logical path module for the source algorithm by joining packageName and sourceAlgorithm. """
85
+ # it follows that `metadata={'evaluateWhen': 'packaging'}`
86
+ return '.'.join([self.packageName, self.sourceAlgorithm])
87
+
88
+ @property # These are not fields, and that annoys me.
89
+ def logicalPathModuleDataclass(self) -> str:
90
+ """ Compute the logical path module for the dataclass by joining packageName and dataclassModule. """
91
+ # it follows that `metadata={'evaluateWhen': 'packaging'}`
92
+ return '.'.join([self.packageName, self.dataclassModule])
93
+
94
+ The = PackageSettings()
113
95
 
114
96
  # =============================================================================
115
-
116
- # Figure out dynamic flow control to synthesized modules https://github.com/hunterhogan/mapFolding/issues/4
117
- concurrencyPackage: str = Z0Z_packageFlow
118
- concurrencyPackage = Z0Z_concurrencyPackage
119
-
120
- # =============================================================================
121
- # The relatively flexible type system needs a different paradigm, but I don't
122
- # know what it should be. The system needs to 1) help optimize computation, 2)
123
- # make it possible to change the basic type of the package (e.g., from numpy
124
- # to superTypePy), 3) make it possible to synthesize the optimized flow of used
125
- # by the package, and 4) make it possible to synthesize arbitrary modules with
126
- # different type systems.
97
+ # Flexible Data Structure System Needs Enhanced Paradigm https://github.com/hunterhogan/mapFolding/issues/9
127
98
 
128
99
  DatatypeLeavesTotal: TypeAlias = int
129
- # this would be uint8, but mapShape (2,2,2,2, 2,2,2,2) has 256 leaves, so generic containers accommodate
100
+ # this would be uint8, but mapShape (2,2,2,2, 2,2,2,2) has 256 leaves, so generic containers must accommodate at least 256 leaves
130
101
  numpyLeavesTotal: TypeAlias = numpy_int16
131
102
 
132
103
  DatatypeElephino: TypeAlias = int
@@ -134,18 +105,12 @@ numpyElephino: TypeAlias = numpy_int16
134
105
 
135
106
  DatatypeFoldsTotal: TypeAlias = int
136
107
  numpyFoldsTotal: TypeAlias = numpy_int64
137
- numpyDtypeDefault = numpyFoldsTotal
138
108
 
139
109
  Array3D: TypeAlias = ndarray[tuple[int, int, int], dtype[numpyLeavesTotal]]
140
110
  Array1DLeavesTotal: TypeAlias = ndarray[tuple[int], dtype[numpyLeavesTotal]]
141
111
  Array1DElephino: TypeAlias = ndarray[tuple[int], dtype[numpyElephino]]
142
112
  Array1DFoldsTotal: TypeAlias = ndarray[tuple[int], dtype[numpyFoldsTotal]]
143
113
 
144
- # =============================================================================
145
- # The right way.
146
- # (The dataclass, not the typing of the dataclass.)
147
- # (Also, my noobplementation of the dataclass certainly needs improvement.)
148
-
149
114
  @dataclasses.dataclass
150
115
  class ComputationState:
151
116
  mapShape: tuple[DatatypeLeavesTotal, ...]
@@ -191,9 +156,9 @@ class ComputationState:
191
156
  leavesTotalAsInt = int(self.leavesTotal)
192
157
 
193
158
  if self.countDimensionsGapped is None:
194
- self.countDimensionsGapped = makeDataContainer(leavesTotalAsInt + 1, numpyElephino)
159
+ self.countDimensionsGapped = makeDataContainer(leavesTotalAsInt + 1, numpyLeavesTotal)
195
160
  if self.gapRangeStart is None:
196
- self.gapRangeStart = makeDataContainer(leavesTotalAsInt + 1, numpyLeavesTotal)
161
+ self.gapRangeStart = makeDataContainer(leavesTotalAsInt + 1, numpyElephino)
197
162
  if self.gapsWhere is None:
198
163
  self.gapsWhere = makeDataContainer(leavesTotalAsInt * leavesTotalAsInt + 1, numpyLeavesTotal)
199
164
  if self.leafAbove is None:
@@ -205,75 +170,50 @@ class ComputationState:
205
170
  self.foldsTotal = DatatypeFoldsTotal(self.foldGroups[0:-1].sum() * self.leavesTotal)
206
171
 
207
172
  # =============================================================================
208
- # The most right way I know how to implement.
209
-
210
- theLogicalPathModuleSourceAlgorithm: str = '.'.join([thePackageName, theSourceAlgorithm])
211
- theLogicalPathModuleDispatcher: str = theLogicalPathModuleSourceAlgorithm
212
- theLogicalPathModuleDataclass: str = '.'.join([thePackageName, theDataclassModule])
213
-
214
- def getSourceAlgorithm() -> ModuleType:
215
- moduleImported: ModuleType = importlib_import_module(theLogicalPathModuleSourceAlgorithm)
216
- return moduleImported
217
-
218
- def getAlgorithmDispatcher() -> Callable[[ComputationState], ComputationState]:
219
- moduleImported: ModuleType = getSourceAlgorithm()
220
- dispatcherCallable = getattr(moduleImported, theDispatcherCallable)
221
- return dispatcherCallable
222
-
223
- def getPathSyntheticModules() -> Path:
224
- return thePathPackage / theModuleOfSyntheticModules
225
173
 
226
174
  # TODO learn how to see this from the user's perspective
227
175
  def getPathJobRootDEFAULT() -> Path:
228
176
  if 'google.colab' in sysModules:
229
177
  pathJobDEFAULT: Path = Path("/content/drive/MyDrive") / "jobs"
230
178
  else:
231
- pathJobDEFAULT = thePathPackage / "jobs"
179
+ pathJobDEFAULT = The.pathPackage / "jobs"
232
180
  return pathJobDEFAULT
233
181
 
234
182
  _datatypePackage: str = ''
235
183
  def getDatatypePackage() -> str:
236
184
  global _datatypePackage
237
185
  if not _datatypePackage:
238
- _datatypePackage = theDatatypePackage
186
+ _datatypePackage = The.datatypePackage
239
187
  return _datatypePackage
240
188
 
241
- def getNumpyDtypeDefault() -> type[signedinteger[Any]]:
242
- return numpyDtypeDefault
243
-
244
189
  # =============================================================================
245
190
  # The coping way.
246
191
 
247
192
  class raiseIfNoneGitHubIssueNumber3(Exception): pass
248
193
 
249
194
  # =============================================================================
250
- # Temporary or transient or something; probably still the wrong way
251
-
252
195
  # THIS IS A STUPID SYSTEM BUT I CAN'T FIGURE OUT AN IMPROVEMENT
253
196
  # NOTE This section for _default_ values probably has value
254
197
  # https://github.com/hunterhogan/mapFolding/issues/4
255
198
  theFormatStrModuleSynthetic = "{packageFlow}Count"
256
199
  theFormatStrModuleForCallableSynthetic = theFormatStrModuleSynthetic + "_{callableTarget}"
257
200
 
258
- theModuleDispatcherSynthetic: str = theFormatStrModuleForCallableSynthetic.format(packageFlow=packageFlowSynthetic, callableTarget=theDispatcherCallable)
259
- theLogicalPathModuleDispatcherSynthetic: str = '.'.join([thePackageName, theModuleOfSyntheticModules, theModuleDispatcherSynthetic])
201
+ theLogicalPathModuleDispatcher: str = The.logicalPathModuleSourceAlgorithm
260
202
 
261
- # =============================================================================
262
- # The most right way I know how to implement.
203
+ theModuleDispatcherSynthetic: str = theFormatStrModuleForCallableSynthetic.format(packageFlow=packageFlowSynthetic, callableTarget=The.dispatcherCallable)
204
+ theLogicalPathModuleDispatcherSynthetic: str = '.'.join([The.packageName, The.moduleOfSyntheticModules, theModuleDispatcherSynthetic])
263
205
 
264
- # https://github.com/hunterhogan/mapFolding/issues/4
265
206
  if Z0Z_packageFlow == packageFlowSynthetic: # pyright: ignore [reportUnnecessaryComparison]
266
207
  # NOTE this as a default value _might_ have value
267
208
  theLogicalPathModuleDispatcher = theLogicalPathModuleDispatcherSynthetic
268
209
 
269
- # https://github.com/hunterhogan/mapFolding/issues/4
270
210
  # dynamically set the return type https://github.com/hunterhogan/mapFolding/issues/5
271
211
  def getPackageDispatcher() -> Callable[[ComputationState], ComputationState]:
272
212
  # NOTE but this part, if the package flow is synthetic, probably needs to be delegated
273
213
  # to the authority for creating _that_ synthetic flow.
274
214
 
275
215
  moduleImported: ModuleType = importlib_import_module(theLogicalPathModuleDispatcher)
276
- dispatcherCallable = getattr(moduleImported, theDispatcherCallable)
216
+ dispatcherCallable = getattr(moduleImported, The.dispatcherCallable)
277
217
  return dispatcherCallable
278
218
 
279
219
  """Technical concepts I am likely using and likely want to use more effectively:
@@ -288,4 +228,13 @@ theSSOT and yourSSOT
288
228
  ----
289
229
  delay realization/instantiation until a concrete value is desired
290
230
  moment of truth: when the value is needed, not when the value is defined
231
+
232
+ ----
233
+ 2025 March 11
234
+ Note to self: fundamental concept in Python:
235
+ Identifiers: scope and resolution, LEGB (Local, Enclosing, Global, Builtin)
236
+ - Local: Inside the function
237
+ - Enclosing: Inside enclosing functions
238
+ - Global: At the uppermost level
239
+ - Builtin: Python's built-in names
291
240
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mapFolding
3
- Version: 0.8.1
3
+ Version: 0.8.2
4
4
  Summary: Map folding algorithm with code transformation framework for optimizing numerical computations
5
5
  Author-email: Hunter Hogan <HunterHogan@pm.me>
6
6
  License: CC-BY-NC-4.0
@@ -99,51 +99,75 @@ Available OEIS sequences:
99
99
  A195646: Number of ways of folding a 3 X 3 X ... X 3 n-dimensional map.
100
100
  ```
101
101
 
102
- ### 2. **Algorithm Zoo** 🦒
102
+ ### 2. **Algorithm Zoo: A Historical and Performance Journey** 🦒
103
103
 
104
- - **Lunnon's 1971 Algorithm**: A painstakingly debugged version of [the original typo-riddled code](https://github.com/hunterhogan/mapFolding/blob/mapFolding/reference/foldings.txt)
105
- - The /reference directory.
106
- - **Numba-JIT Accelerated**: Up to 1000× faster than pure Python ([benchmarks](https://github.com/hunterhogan/mapFolding/blob/mapFolding/notes/Speed%20highlights.md))
104
+ This package offers a comprehensive collection of map folding algorithm implementations that showcase its evolution from historical origins to high-performance computation:
107
105
 
108
- ### 3. **For Researchers and Power Users** 🔬
106
+ - **Historical Implementations**:
107
+ - Carefully restored versions of Lunnon's 1971 original [algorithm](https://github.com/hunterhogan/mapFolding/blob/mapFolding/reference/foldings.txt) with corrections
108
+ - Atlas Autocode reconstruction in the `reference/foldings.AA` file
109
109
 
110
- This package provides a sophisticated code transformation framework that can turn readable algorithm implementations into highly-optimized computational engines:
110
+ - **Direct Translations**:
111
+ - Python translations following the original control flow (`lunnanWhile.py`)
112
+ - NumPy-based vectorized implementations (`lunnanNumpy.py`)
111
113
 
112
- - **Algorithmic Exploration**: Study the core algorithm in `theDao.py`, which uses a functional state-transformation approach with clear, isolated functions
113
- - **Performance Optimization**: Generate specialized implementations with the `someAssemblyRequired` transformation pipeline:
114
- - AST-based code analysis and manipulation
115
- - Dataclass "shattering" to decompose complex state objects into primitive components
116
- - Just-in-time compilation with Numba and various optimization profiles
117
- - LLVM IR extraction for low-level algorithmic analysis
114
+ - **Modern Implementations**:
115
+ - Java port adaptations (`irvineJavaPort.py`) providing cleaner procedural implementations
116
+ - Experimental JAX version (`jaxCount.py`) exploring GPU acceleration potential
117
+ - Semantically decomposed version (`flattened.py`) with clear function boundaries
118
118
 
119
- - **Extensible Design**: The transformation framework is abstract and generic, enabling:
120
- - Creation of new optimization targets beyond the included Numba implementation
121
- - Customization of compilation parameters and optimization levels
122
- - Development of specialized algorithms for specific map dimensions
119
+ - **Performance Optimized**:
120
+ - Numba-JIT accelerated implementations up to 1000× faster than pure Python (see [benchmarks](https://github.com/hunterhogan/mapFolding/blob/mapFolding/notes/Speed%20highlights.md))
121
+ - Algorithmic optimizations showcasing subtle yet powerful performance differences (`total_countPlus1vsPlusN.py`)
123
122
 
124
- ### 4. **Customization and Extension Guide**
123
+ The `reference` directory serves as both a historical archive and an educational resource for understanding algorithm evolution.
125
124
 
126
- The package architecture supports multiple levels of customization:
125
+ ### 3. **Algorithmic Transformation: From Readability to Speed** 🔬
127
126
 
128
- - **Basic Usage**: Work with the high-level API in `basecamp.py` for standard computations
129
- - **Algorithm Modification**:
127
+ The package provides a sophisticated transformation framework that bridges the gap between human-readable algorithms and high-performance computation:
128
+
129
+ - **Core Algorithm Understanding**:
130
+ - Study the functional state-transformation approach in `theDao.py` with clear, isolated functions
131
+ - Explore the semantic decomposition in `reference/flattened.py` to understand algorithm sections
132
+
133
+ - **Code Transformation Pipeline**:
134
+ - **AST Manipulation**: Analyzes and transforms the algorithm's abstract syntax tree
135
+ - **Dataclass "Shattering"**: Decomposes complex state objects into primitive components
136
+ - **Optimization Applications**: Applies domain-specific optimizations for numerical computation
137
+ - **LLVM Integration**: Extracts LLVM IR for low-level algorithmic analysis
138
+
139
+ - **Performance Breakthroughs**:
140
+ - Learn why nearly identical algorithms can have dramatically different performance (`total_countPlus1vsPlusN.py`)
141
+ - See how memory layout and increment strategy impact computation speed
142
+ - Understand the batching technique that yields order-of-magnitude improvements
143
+
144
+ ### 4. **Multi-Level Architecture: From Simple API to Full Customization**
145
+
146
+ The package's architecture supports multiple levels of engagement:
147
+
148
+ - **Basic Usage**:
149
+ - Work with the high-level API in `basecamp.py` for standard computations
150
+ - Access OEIS sequence calculations with minimal code
151
+
152
+ - **Algorithm Exploration**:
153
+ - Compare different implementations in the `reference` directory to understand trade-offs
130
154
  - Modify the core algorithm in `theDao.py` while preserving its functional approach
131
155
  - Configure system-wide settings in `theSSOT.py` to adjust data types and performance characteristics
132
- - Use utility functions in `beDRY.py` for common operations
133
156
 
134
157
  - **Advanced Transformation**:
135
- - The `someAssemblyRequired` package provides tools to transform code at the AST level:
136
- - `transformationTools.py` contains utilities for AST manipulation and code generation
137
- - `transformDataStructures.py` handles complex data structure transformations
138
- - `ingredientsNumba.py` provides Numba-specific configuration profiles
139
- - `synthesizeNumbaFlow.py` orchestrates the transformation process
158
+ - Use the `someAssemblyRequired` package to transform algorithms at the AST level
159
+ - Create optimized variants with different compilation settings using:
160
+ - `transformationTools.py` for AST manipulation
161
+ - `transformDataStructures.py` for complex data structure transformations
162
+ - `ingredientsNumba.py` for Numba-specific optimization profiles
163
+ - `synthesizeNumbaFlow.py` to orchestrate the transformation process
140
164
 
141
165
  - **Custom Deployment**:
142
166
  - Generate specialized implementations for specific dimensions
143
- - Create optimized modules that can be executed as standalone scripts
144
- - Extract LLVM IR for further analysis or optimization
167
+ - Create optimized standalone modules for production use
168
+ - Extract LLVM IR for further analysis and optimization
145
169
 
146
- The package's multi-level design allows you to start with simple API calls and progressively delve deeper into optimization as your computational needs grow.
170
+ The package's multi-level design allows you to start with simple API calls and progressively explore deeper optimization techniques as your computational needs grow.
147
171
 
148
172
  ## Map-folding Video
149
173
 
@@ -0,0 +1,39 @@
1
+ mapFolding/__init__.py,sha256=hYxPUBU6A1_XCbKEseSDamooTsb1mzN_XHqaRLPvpGk,1701
2
+ mapFolding/basecamp.py,sha256=uPwbb_fi8zqqBbVjb355qanSNUqqJ9aefcf_nrvA7qI,4510
3
+ mapFolding/beDRY.py,sha256=UhH52BryHQNRjphf_PirtMkV45rhdemdC9PmnpACq7I,9397
4
+ mapFolding/filesystem.py,sha256=-pYpWugd0p3TrAz7xf9YIJW-pn1X-iRCGtJgEAF9Rns,5923
5
+ mapFolding/noHomeYet.py,sha256=UKZeWlyn0SKlF9dhYoud7E6gWXpiSEekZOOoJp88WeI,1362
6
+ mapFolding/oeis.py,sha256=TbY8KtAGbQlT6eEsa_7HVMF7bMLN-aBFKclyTMHfqHk,12615
7
+ mapFolding/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ mapFolding/theDao.py,sha256=Blzm5j24x1BE2nvgXjdzHEeuc2na6kAH9b_eP6PcwlI,9836
9
+ mapFolding/theSSOT.py,sha256=HIU9UVKP0dXlxIO791z_tl1R7r5LiEcKnA5AI7H9SmU,12414
10
+ mapFolding/reference/__init__.py,sha256=7NYyWXyYYSdia2Q8SFJTcD0KvoUBLvvfuvTsaqcvchM,1777
11
+ mapFolding/reference/flattened.py,sha256=QK1xG9SllqCoi68e86Hyl9d9ATUAAFNpTQI-3zmcp5I,16072
12
+ mapFolding/reference/hunterNumba.py,sha256=espFiX92EPZ1Ub1YQVoBnNYvh2kFg1HR6Qa4djx8Ixg,7253
13
+ mapFolding/reference/irvineJavaPort.py,sha256=UEfIX4QbPLl5jnyfYIyX5YRR3_rYvPUikK8jLehsFko,4076
14
+ mapFolding/reference/jaxCount.py,sha256=TuDNKOnyhQfuixKmIxO9Algv7dvy7KMGhgsV3h96FGE,14853
15
+ mapFolding/reference/lunnanNumpy.py,sha256=mMgrgbrBpe4nmo72ThEI-MGH0OwEHmfMPczSXHp2qKo,4357
16
+ mapFolding/reference/lunnanWhile.py,sha256=ZL8GAQtPs5nJZSgoDl5USrLSS_zs03y98y1Z9E4jOmQ,3799
17
+ mapFolding/reference/rotatedEntryPoint.py,sha256=5ughpKUT2JQhoAKgoDUdYNjgWQYPGV8v-7dWEAdDmfE,10274
18
+ mapFolding/reference/total_countPlus1vsPlusN.py,sha256=yJZAVLVdoXqHag2_N6_6CT-Q6HXBgRro-eny93-Rlpw,9307
19
+ mapFolding/someAssemblyRequired/__init__.py,sha256=xA5a-nZjXIwcqEOig5PEZSxde4_m3JJ5Pb0CN4aiRjw,2488
20
+ mapFolding/someAssemblyRequired/getLLVMforNoReason.py,sha256=bGI8RZY-RnyR9TNF0r0OXwA6fm4TYH2cHy7WzhsnddQ,1895
21
+ mapFolding/someAssemblyRequired/ingredientsNumba.py,sha256=g6Z7t35NpoDskzm0OLwTQhHw5CYiYktVYxI2NhCQHww,8435
22
+ mapFolding/someAssemblyRequired/synthesizeNumbaFlow.py,sha256=0179DMPoGJGP6AHs-nIac_aSfVBgpDyuaeCBVKlPRe8,10691
23
+ mapFolding/someAssemblyRequired/synthesizeNumbaJobVESTIGIAL.py,sha256=RBSrtr7US2P7mkY-EA-b2WIOxjs2b0WJaCln1ERxOcI,22314
24
+ mapFolding/someAssemblyRequired/transformDataStructures.py,sha256=Uth-WLzCTJTQkU15lsrBGf1ld7nqgLK42DUYM6-Q-n8,8605
25
+ mapFolding/someAssemblyRequired/transformationTools.py,sha256=n_lH9B7E871htRRRVJIDqGuT4WTmGiIgBb3ZjsY7ZjA,40689
26
+ mapFolding/syntheticModules/numbaCount_doTheNeedful.py,sha256=52RuwJVH2fROvWU2dT8wYcQvLgRuvkNZPq01kujCC_U,15725
27
+ mapfolding-0.8.2.dist-info/licenses/LICENSE,sha256=NxH5Y8BdC-gNU-WSMwim3uMbID2iNDXJz7fHtuTdXhk,19346
28
+ tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
+ tests/conftest.py,sha256=hTpUTW7MtYGP5aeQnnoZMKgTEGCKdLJ8Fnmnv9d4NJw,11115
30
+ tests/test_computations.py,sha256=RHHByyuC8w-qbaag4Iqo_QNYm_7A-9BslbstMOdbZbU,3329
31
+ tests/test_filesystem.py,sha256=Kou0gj5T72oISao6umYfU6L_W5Hi7QS9_IxTv2hU0Pw,3147
32
+ tests/test_oeis.py,sha256=uxvwmgbnylSDdsVJfuAT0LuYLbIVFwSgdLxHm-xUGBM,5043
33
+ tests/test_other.py,sha256=4iF6JJ192BtDZUEZ0avbVRprCwfCUSOUC8GfCPrGS8M,4232
34
+ tests/test_tasks.py,sha256=hkZygihT8bCEO2zc-2VcxReQrZJBwgLNbYx0YP4lTDg,2853
35
+ mapfolding-0.8.2.dist-info/METADATA,sha256=8tSQtHxSLzIjIINZCw4EExiSeQV_wpLQXvf4gQ4xFNo,9143
36
+ mapfolding-0.8.2.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
37
+ mapfolding-0.8.2.dist-info/entry_points.txt,sha256=F3OUeZR1XDTpoH7k3wXuRb3KF_kXTTeYhu5AGK1SiOQ,146
38
+ mapfolding-0.8.2.dist-info/top_level.txt,sha256=1gP2vFaqPwHujGwb3UjtMlLEGN-943VSYFR7V4gDqW8,17
39
+ mapfolding-0.8.2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (77.0.1)
2
+ Generator: setuptools (77.0.3)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
tests/conftest.py CHANGED
@@ -1,8 +1,14 @@
1
+ from importlib import import_module as importlib_import_module
1
2
  from collections.abc import Callable, Generator, Sequence
2
- from mapFolding.theSSOT import getAlgorithmDispatcher, getSourceAlgorithm, getPackageDispatcher, theModuleOfSyntheticModules, raiseIfNoneGitHubIssueNumber3
3
+ from types import ModuleType
4
+
5
+ import numpy
6
+ from mapFolding.theSSOT import ComputationState, The, getPackageDispatcher
3
7
  from mapFolding.beDRY import getLeavesTotal, validateListDimensions, makeDataContainer
4
8
  from mapFolding.oeis import oeisIDsImplemented, settingsOEIS
5
- from pathlib import Path
9
+ from mapFolding.someAssemblyRequired import RecipeSynthesizeFlow
10
+ from mapFolding.someAssemblyRequired.synthesizeNumbaFlow import makeNumbaFlow
11
+ from pathlib import Path, PurePosixPath
6
12
  from typing import Any, ContextManager
7
13
  import importlib.util
8
14
  import pytest
@@ -13,7 +19,6 @@ import uuid
13
19
 
14
20
  # SSOT for test data paths and filenames
15
21
  pathDataSamples = Path("tests/dataSamples")
16
- # NOTE `tmp` is not a diminutive form of temporary: it signals a technical term. And "temp" is strongly disfavored.
17
22
  pathTmpRoot: Path = pathDataSamples / "tmp"
18
23
 
19
24
  # The registrar maintains the register of temp files
@@ -142,7 +147,7 @@ def mockBenchmarkTimer() -> Generator[unittest.mock.MagicMock | unittest.mock.As
142
147
  def mockFoldingFunction() -> Callable[..., Callable[..., None]]:
143
148
  """Creates a mock function that simulates _countFolds behavior."""
144
149
  def make_mock(foldsValue: int, listDimensions: list[int]) -> Callable[..., None]:
145
- mock_array = makeDataContainer(2)
150
+ mock_array = makeDataContainer(2, numpy.int32)
146
151
  mock_array[0] = foldsValue
147
152
  mapShape = validateListDimensions(listDimensions)
148
153
  mock_array[-1] = getLeavesTotal(mapShape)
@@ -192,6 +197,11 @@ def useThisDispatcher() -> Generator[Callable[..., None], Any, None]:
192
197
  yield patchDispatcher
193
198
  basecamp.getPackageDispatcher = dispatcherOriginal
194
199
 
200
+ def getAlgorithmDispatcher() -> Callable[[ComputationState], ComputationState]:
201
+ moduleImported: ModuleType = importlib_import_module(The.logicalPathModuleSourceAlgorithm)
202
+ dispatcherCallable = getattr(moduleImported, The.dispatcherCallable)
203
+ return dispatcherCallable
204
+
195
205
  @pytest.fixture
196
206
  def useAlgorithmSourceDispatcher(useThisDispatcher: Callable[..., Any]) -> Generator[None, None, None]:
197
207
  """Temporarily patches getDispatcherCallable to return the algorithm dispatcher."""
@@ -199,35 +209,35 @@ def useAlgorithmSourceDispatcher(useThisDispatcher: Callable[..., Any]) -> Gener
199
209
  yield
200
210
 
201
211
  @pytest.fixture
202
- def syntheticDispatcherFixture(useThisDispatcher: Callable[..., Any]) -> Callable[..., Any]:
203
- listCallablesInline = listNumbaCallableDispatchees
204
- callableDispatcher = True
205
- algorithmSource = getSourceAlgorithm()
206
- relativePathWrite = theModuleOfSyntheticModules
207
- filenameModuleWrite = 'pytestCount.py'
208
- formatFilenameWrite = "pytest_{callableTarget}.py"
209
- listSynthesizedModules: list[YouOughtaKnow] = makeFlowNumbaOptimized(listCallablesInline, callableDispatcher, algorithmSource, relativePathWrite, filenameModuleWrite, formatFilenameWrite)
210
- dispatcherSynthetic: YouOughtaKnow | None = None
211
- for stuff in listSynthesizedModules:
212
- registrarRecordsTmpObject(stuff.pathFilenameForMe)
213
- if stuff.callableSynthesized not in listCallablesInline:
214
- dispatcherSynthetic = stuff
215
-
216
- if dispatcherSynthetic is None:
217
- raise raiseIfNoneGitHubIssueNumber3
218
-
219
- dispatcherSpec = importlib.util.spec_from_file_location(dispatcherSynthetic.callableSynthesized, dispatcherSynthetic.pathFilenameForMe)
220
- if dispatcherSpec is None:
221
- raise ImportError(f"{dispatcherSynthetic.pathFilenameForMe=}")
222
- if dispatcherSpec.loader is None:
223
- raise ImportError(f"Failed to get loader for module {dispatcherSynthetic.pathFilenameForMe}")
224
-
225
- dispatcherModule = importlib.util.module_from_spec(dispatcherSpec)
226
- dispatcherSpec.loader.exec_module(dispatcherModule)
227
- callableDispatcherSynthetic = getattr(dispatcherModule, dispatcherSynthetic.callableSynthesized)
228
-
229
- useThisDispatcher(callableDispatcherSynthetic)
230
- return callableDispatcherSynthetic
212
+ def syntheticDispatcherFixture(useThisDispatcher: Callable[..., Any], pathTmpTesting: Path) -> Callable[..., Any]:
213
+ """Generate synthetic Numba-optimized dispatcher module and patch the dispatcher"""
214
+ # Configure synthesis flow to use test directory
215
+ recipeFlow = RecipeSynthesizeFlow(
216
+ pathPackage=PurePosixPath(pathTmpTesting.absolute()),
217
+ Z0Z_flowLogicalPathRoot=None,
218
+ moduleDispatcher="test_dispatcher",
219
+ # Figure out dynamic flow control to synthesized modules https://github.com/hunterhogan/mapFolding/issues/4
220
+ # dispatcherCallable="dispatcherSynthetic",
221
+ )
222
+
223
+ # Generate optimized module in test directory
224
+ makeNumbaFlow(recipeFlow)
225
+
226
+ # Import synthesized dispatcher
227
+ importlibSpecificationDispatcher = importlib.util.spec_from_file_location(
228
+ recipeFlow.moduleDispatcher,
229
+ Path(recipeFlow.pathFilenameDispatcher),
230
+ )
231
+ if importlibSpecificationDispatcher is None or importlibSpecificationDispatcher.loader is None:
232
+ raise ImportError("Failed to load synthetic dispatcher module")
233
+
234
+ moduleSpecificationDispatcher = importlib.util.module_from_spec(importlibSpecificationDispatcher)
235
+ importlibSpecificationDispatcher.loader.exec_module(moduleSpecificationDispatcher)
236
+ callableDispatcherSynthetic = getattr(moduleSpecificationDispatcher, recipeFlow.dispatcherCallable)
237
+
238
+ # Patch dispatcher and return callable
239
+ useThisDispatcher(callableDispatcherSynthetic)
240
+ return callableDispatcherSynthetic
231
241
 
232
242
  def uniformTestMessage(expected: Any, actual: Any, functionName: str, *arguments: Any) -> str:
233
243
  """Format assertion message for any test comparison."""
@@ -23,6 +23,12 @@ def test_aOFn_calculate_value(oeisID: str) -> None:
23
23
  for n in settingsOEIS[oeisID]['valuesTestValidation']:
24
24
  standardizedEqualToCallableReturn(settingsOEIS[oeisID]['valuesKnown'][n], oeisIDfor_n, oeisID, n)
25
25
 
26
+ def test_syntheticParallel(syntheticDispatcherFixture: None, listDimensionsTestParallelization: list[int]):
27
+ standardizedEqualToCallableReturn(getFoldsTotalKnown(tuple(listDimensionsTestParallelization)), countFolds, listDimensionsTestParallelization, None, 'maximum')
28
+
29
+ def test_syntheticSequential(syntheticDispatcherFixture: None, listDimensionsTestCountFolds: list[int]) -> None:
30
+ standardizedEqualToCallableReturn(getFoldsTotalKnown(tuple(listDimensionsTestCountFolds)), countFolds, listDimensionsTestCountFolds)
31
+
26
32
  # @pytest.mark.parametrize('pathFilenameTmpTesting', ['.py'], indirect=True)
27
33
  # def test_writeJobNumba(listDimensionsTestCountFolds: list[int], pathFilenameTmpTesting: Path) -> None:
28
34
  # from mapFolding.syntheticModules import numbaCount
@@ -44,10 +50,4 @@ def test_aOFn_calculate_value(oeisID: str) -> None:
44
50
 
45
51
  # pathFilenameFoldsTotal = getPathFilenameFoldsTotal(listDimensionsTestCountFolds)
46
52
  # registrarRecordsTmpObject(pathFilenameFoldsTotal)
47
- # standardizedEqualTo(str(foldsTotalKnown[tuple(listDimensionsTestCountFolds)]), pathFilenameFoldsTotal.read_text().strip)
48
-
49
- # def test_syntheticParallel(syntheticDispatcherFixture: None, listDimensionsTestParallelization: list[int], foldsTotalKnown: dict[tuple[int, ...], int]):
50
- # standardizedEqualTo(foldsTotalKnown[tuple(listDimensionsTestParallelization)], countFolds, listDimensionsTestParallelization, None, 'maximum')
51
-
52
- # def test_syntheticSequential(syntheticDispatcherFixture: None, listDimensionsTestCountFolds: list[int], foldsTotalKnown: dict[tuple[int, ...], int]):
53
- # standardizedEqualTo(foldsTotalKnown[tuple(listDimensionsTestCountFolds)], countFolds, listDimensionsTestCountFolds)
53
+ # standardizedEqualToCallableReturn(str(getFoldsTotalKnown(tuple(listDimensionsTestCountFolds))), pathFilenameFoldsTotal.read_text().strip)
tests/test_other.py CHANGED
@@ -1,5 +1,6 @@
1
1
  from collections.abc import Callable
2
2
  from mapFolding.beDRY import getLeavesTotal, setCPUlimit, validateListDimensions
3
+ from mapFolding.theSSOT import The
3
4
  from tests.conftest import standardizedEqualToCallableReturn
4
5
  from typing import Any, Literal
5
6
  from Z0Z_tools import intInnit
@@ -78,6 +79,5 @@ def testOopsieKwargsie(nameOfTest: str, callablePytest: Callable[[], None]) -> N
78
79
  (1, 1),
79
80
  ])
80
81
  def test_setCPUlimit(CPUlimit: None | float | bool | Literal[4] | Literal[-2] | Literal[0] | Literal[1], expectedLimit: Any | int) -> None:
81
- from mapFolding.theSSOT import concurrencyPackage
82
- if concurrencyPackage == 'numba':
82
+ if The.concurrencyPackage == 'numba':
83
83
  standardizedEqualToCallableReturn(expectedLimit, setCPUlimit, CPUlimit)