executorlib 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. executorlib-0.0.1/LICENSE +29 -0
  2. executorlib-0.0.1/MANIFEST.in +1 -0
  3. executorlib-0.0.1/PKG-INFO +193 -0
  4. executorlib-0.0.1/README.md +127 -0
  5. executorlib-0.0.1/executorlib/__init__.py +225 -0
  6. executorlib-0.0.1/executorlib/_version.py +21 -0
  7. executorlib-0.0.1/executorlib/backend/__init__.py +0 -0
  8. executorlib-0.0.1/executorlib/backend/cache_parallel.py +40 -0
  9. executorlib-0.0.1/executorlib/backend/cache_serial.py +6 -0
  10. executorlib-0.0.1/executorlib/backend/interactive_parallel.py +91 -0
  11. executorlib-0.0.1/executorlib/backend/interactive_serial.py +65 -0
  12. executorlib-0.0.1/executorlib/cache/__init__.py +0 -0
  13. executorlib-0.0.1/executorlib/cache/executor.py +28 -0
  14. executorlib-0.0.1/executorlib/cache/hdf.py +83 -0
  15. executorlib-0.0.1/executorlib/cache/shared.py +217 -0
  16. executorlib-0.0.1/executorlib/interactive/__init__.py +168 -0
  17. executorlib-0.0.1/executorlib/interactive/backend.py +76 -0
  18. executorlib-0.0.1/executorlib/interactive/dependencies.py +67 -0
  19. executorlib-0.0.1/executorlib/interactive/executor.py +112 -0
  20. executorlib-0.0.1/executorlib/interactive/flux.py +79 -0
  21. executorlib-0.0.1/executorlib/shared/__init__.py +24 -0
  22. executorlib-0.0.1/executorlib/shared/communication.py +195 -0
  23. executorlib-0.0.1/executorlib/shared/executor.py +635 -0
  24. executorlib-0.0.1/executorlib/shared/inputcheck.py +121 -0
  25. executorlib-0.0.1/executorlib/shared/interface.py +154 -0
  26. executorlib-0.0.1/executorlib/shared/plot.py +82 -0
  27. executorlib-0.0.1/executorlib/shared/thread.py +31 -0
  28. executorlib-0.0.1/executorlib/shell/__init__.py +7 -0
  29. executorlib-0.0.1/executorlib/shell/executor.py +114 -0
  30. executorlib-0.0.1/executorlib/shell/interactive.py +181 -0
  31. executorlib-0.0.1/executorlib.egg-info/PKG-INFO +193 -0
  32. executorlib-0.0.1/executorlib.egg-info/SOURCES.txt +57 -0
  33. executorlib-0.0.1/executorlib.egg-info/dependency_links.txt +1 -0
  34. executorlib-0.0.1/executorlib.egg-info/requires.txt +18 -0
  35. executorlib-0.0.1/executorlib.egg-info/top_level.txt +1 -0
  36. executorlib-0.0.1/pyproject.toml +62 -0
  37. executorlib-0.0.1/setup.cfg +4 -0
  38. executorlib-0.0.1/setup.py +8 -0
  39. executorlib-0.0.1/tests/test_backend_serial.py +85 -0
  40. executorlib-0.0.1/tests/test_cache_executor_mpi.py +41 -0
  41. executorlib-0.0.1/tests/test_cache_executor_serial.py +110 -0
  42. executorlib-0.0.1/tests/test_cache_hdf.py +66 -0
  43. executorlib-0.0.1/tests/test_cache_shared.py +100 -0
  44. executorlib-0.0.1/tests/test_dependencies_executor.py +203 -0
  45. executorlib-0.0.1/tests/test_executor_backend_flux.py +115 -0
  46. executorlib-0.0.1/tests/test_executor_backend_mpi.py +80 -0
  47. executorlib-0.0.1/tests/test_executor_backend_mpi_noblock.py +78 -0
  48. executorlib-0.0.1/tests/test_executor_conda.py +76 -0
  49. executorlib-0.0.1/tests/test_flux_executor.py +154 -0
  50. executorlib-0.0.1/tests/test_integration_pyiron_workflow.py +236 -0
  51. executorlib-0.0.1/tests/test_local_executor.py +483 -0
  52. executorlib-0.0.1/tests/test_local_executor_future.py +141 -0
  53. executorlib-0.0.1/tests/test_shared_backend.py +114 -0
  54. executorlib-0.0.1/tests/test_shared_communication.py +100 -0
  55. executorlib-0.0.1/tests/test_shared_executorbase.py +23 -0
  56. executorlib-0.0.1/tests/test_shared_input_check.py +61 -0
  57. executorlib-0.0.1/tests/test_shared_thread.py +15 -0
  58. executorlib-0.0.1/tests/test_shell_executor.py +86 -0
  59. executorlib-0.0.1/tests/test_shell_interactive.py +70 -0
@@ -0,0 +1,29 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2022, Jan Janssen
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ * Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ * Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ * Neither the name of the copyright holder nor the names of its
17
+ contributors may be used to endorse or promote products derived from
18
+ this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1 @@
1
+ include LICENSE
@@ -0,0 +1,193 @@
1
+ Metadata-Version: 2.1
2
+ Name: executorlib
3
+ Version: 0.0.1
4
+ Summary: Scale serial and MPI-parallel python functions over hundreds of compute nodes all from within a jupyter notebook or serial python process.
5
+ Author-email: Jan Janssen <janssen@lanl.gov>
6
+ License: BSD 3-Clause License
7
+
8
+ Copyright (c) 2022, Jan Janssen
9
+ All rights reserved.
10
+
11
+ Redistribution and use in source and binary forms, with or without
12
+ modification, are permitted provided that the following conditions are met:
13
+
14
+ * Redistributions of source code must retain the above copyright notice, this
15
+ list of conditions and the following disclaimer.
16
+
17
+ * Redistributions in binary form must reproduce the above copyright notice,
18
+ this list of conditions and the following disclaimer in the documentation
19
+ and/or other materials provided with the distribution.
20
+
21
+ * Neither the name of the copyright holder nor the names of its
22
+ contributors may be used to endorse or promote products derived from
23
+ this software without specific prior written permission.
24
+
25
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
29
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35
+
36
+ Project-URL: Homepage, https://github.com/pyiron/executorlib
37
+ Project-URL: Documentation, https://executorlib.readthedocs.io
38
+ Project-URL: Repository, https://github.com/pyiron/executorlib
39
+ Keywords: pyiron
40
+ Classifier: Development Status :: 5 - Production/Stable
41
+ Classifier: Topic :: Scientific/Engineering :: Physics
42
+ Classifier: License :: OSI Approved :: BSD License
43
+ Classifier: Intended Audience :: Science/Research
44
+ Classifier: Operating System :: OS Independent
45
+ Classifier: Programming Language :: Python :: 3.9
46
+ Classifier: Programming Language :: Python :: 3.10
47
+ Classifier: Programming Language :: Python :: 3.11
48
+ Classifier: Programming Language :: Python :: 3.12
49
+ Requires-Python: <3.13,>=3.9
50
+ Description-Content-Type: text/markdown
51
+ License-File: LICENSE
52
+ Requires-Dist: cloudpickle<=3.0.0,>=2.0.0
53
+ Requires-Dist: pyzmq<=26.0.3,>=25.0.0
54
+ Provides-Extra: conda
55
+ Requires-Dist: conda_subprocess<=0.0.4,>=0.0.3; extra == "conda"
56
+ Provides-Extra: mpi
57
+ Requires-Dist: mpi4py<=3.1.6,>=3.1.4; extra == "mpi"
58
+ Provides-Extra: hdf
59
+ Requires-Dist: h5py<=3.11.0,>=3.6.0; extra == "hdf"
60
+ Requires-Dist: h5io<=0.2.3,>=0.2.1; extra == "hdf"
61
+ Provides-Extra: graph
62
+ Requires-Dist: pygraphviz<=1.13,>=1.10; extra == "graph"
63
+ Requires-Dist: matplotlib<=3.9.1,>=3.5.3; extra == "graph"
64
+ Requires-Dist: networkx<=3.3,>=2.8.8; extra == "graph"
65
+ Requires-Dist: ipython<=8.26.0,>=7.33.0; extra == "graph"
66
+
67
+ # executorlib
68
+ [![Unittests](https://github.com/pyiron/executorlib/actions/workflows/unittest-openmpi.yml/badge.svg)](https://github.com/pyiron/executorlib/actions/workflows/unittest-openmpi.yml)
69
+ [![Coverage Status](https://coveralls.io/repos/github/pyiron/executorlib/badge.svg?branch=main)](https://coveralls.io/github/pyiron/executorlib?branch=main)
70
+ [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/pyiron/executorlib/HEAD?labpath=notebooks%2Fexamples.ipynb)
71
+
72
+ ## Challenges
73
+ In high performance computing (HPC) the Python programming language is commonly used as high-level language to
74
+ orchestrate the coupling of scientific applications. Still the efficient usage of highly parallel HPC clusters remains
75
+ challenging, in primarily three aspects:
76
+
77
+ * **Communication**: Distributing python function calls over hundreds of compute node and gathering the results on a
78
+ shared file system is technically possible, but highly inefficient. A socket-based communication approach is
79
+ preferable.
80
+ * **Resource Management**: Assigning Python functions to GPUs or executing Python functions on multiple CPUs using the
81
+ message passing interface (MPI) requires major modifications to the python workflow.
82
+ * **Integration**: Existing workflow libraries implement a secondary the job management on the Python level rather than
83
+ leveraging the existing infrastructure provided by the job scheduler of the HPC.
84
+
85
+ ### executorlib is ...
86
+ In a given HPC allocation the `executorlib` library addresses these challenges by extending the Executor interface
87
+ of the standard Python library to support the resource assignment in the HPC context. Computing resources can either be
88
+ assigned on a per function call basis or as a block allocation on a per Executor basis. The `executorlib` library
89
+ is built on top of the [flux-framework](https://flux-framework.org) to enable fine-grained resource assignment. In
90
+ addition, [Simple Linux Utility for Resource Management (SLURM)](https://slurm.schedmd.com) is supported as alternative
91
+ queuing system and for workstation installations `executorlib` can be installed without a job scheduler.
92
+
93
+ ### executorlib is not ...
94
+ The executorlib library is not designed to request an allocation from the job scheduler of an HPC. Instead within a given
95
+ allocation from the job scheduler the `executorlib` library can be employed to distribute a series of python
96
+ function calls over the available computing resources to achieve maximum computing resource utilization.
97
+
98
+ ## Example
99
+ The following examples illustrates how `executorlib` can be used to distribute a series of MPI parallel function calls
100
+ within a queuing system allocation. `example.py`:
101
+ ```python
102
+ import flux.job
103
+ from executorlib import Executor
104
+
105
+ def calc(i):
106
+ from mpi4py import MPI
107
+ size = MPI.COMM_WORLD.Get_size()
108
+ rank = MPI.COMM_WORLD.Get_rank()
109
+ return i, size, rank
110
+
111
+ with flux.job.FluxExecutor() as flux_exe:
112
+ with Executor(max_cores=2, cores_per_worker=2, executor=flux_exe) as exe:
113
+ fs = exe.submit(calc, 3)
114
+ print(fs.result())
115
+ ```
116
+ This example can be executed using:
117
+ ```
118
+ python example.py
119
+ ```
120
+ Which returns:
121
+ ```
122
+ >>> [(0, 2, 0), (0, 2, 1)], [(1, 2, 0), (1, 2, 1)]
123
+ ```
124
+ The important part in this example is that [mpi4py](https://mpi4py.readthedocs.io) is only used in the `calc()`
125
+ function, not in the python script, consequently it is not necessary to call the script with `mpiexec` but instead
126
+ a call with the regular python interpreter is sufficient. This highlights how `executorlib` allows the users to
127
+ parallelize one function at a time and not having to convert their whole workflow to use [mpi4py](https://mpi4py.readthedocs.io).
128
+ The same code can also be executed inside a jupyter notebook directly which enables an interactive development process.
129
+
130
+ The interface of the standard [concurrent.futures.Executor](https://docs.python.org/3/library/concurrent.futures.html#module-concurrent.futures)
131
+ is extended by adding the option `cores_per_worker=2` to assign multiple MPI ranks to each function call. To create two
132
+ workers the maximum number of cores can be increased to `max_cores=4`. In this case each worker receives two cores
133
+ resulting in a total of four CPU cores being utilized.
134
+
135
+ After submitting the function `calc()` with the corresponding parameter to the executor `exe.submit(calc, 0)`
136
+ a python [`concurrent.futures.Future`](https://docs.python.org/3/library/concurrent.futures.html#future-objects) is
137
+ returned. Consequently, the `executorlib.Executor` can be used as a drop-in replacement for the
138
+ [`concurrent.futures.Executor`](https://docs.python.org/3/library/concurrent.futures.html#module-concurrent.futures)
139
+ which allows the user to add parallelism to their workflow one function at a time.
140
+
141
+ ## Disclaimer
142
+ While we try to develop a stable and reliable software library, the development remains a opensource project under the
143
+ BSD 3-Clause License without any warranties::
144
+ ```
145
+ BSD 3-Clause License
146
+
147
+ Copyright (c) 2022, Jan Janssen
148
+ All rights reserved.
149
+
150
+ Redistribution and use in source and binary forms, with or without
151
+ modification, are permitted provided that the following conditions are met:
152
+
153
+ * Redistributions of source code must retain the above copyright notice, this
154
+ list of conditions and the following disclaimer.
155
+
156
+ * Redistributions in binary form must reproduce the above copyright notice,
157
+ this list of conditions and the following disclaimer in the documentation
158
+ and/or other materials provided with the distribution.
159
+
160
+ * Neither the name of the copyright holder nor the names of its
161
+ contributors may be used to endorse or promote products derived from
162
+ this software without specific prior written permission.
163
+
164
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
165
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
166
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
167
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
168
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
169
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
170
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
171
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
172
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
173
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
174
+ ```
175
+
176
+ # Documentation
177
+ * [Installation](https://executorlib.readthedocs.io/en/latest/installation.html)
178
+ * [Compatible Job Schedulers](https://executorlib.readthedocs.io/en/latest/installation.html#compatible-job-schedulers)
179
+ * [executorlib with Flux Framework](https://executorlib.readthedocs.io/en/latest/installation.html#executorlib-with-flux-framework)
180
+ * [Test Flux Framework](https://executorlib.readthedocs.io/en/latest/installation.html#test-flux-framework)
181
+ * [Without Flux Framework](https://executorlib.readthedocs.io/en/latest/installation.html#without-flux-framework)
182
+ * [Examples](https://executorlib.readthedocs.io/en/latest/examples.html)
183
+ * [Compatibility](https://executorlib.readthedocs.io/en/latest/examples.html#compatibility)
184
+ * [Resource Assignment](https://executorlib.readthedocs.io/en/latest/examples.html#resource-assignment)
185
+ * [Data Handling](https://executorlib.readthedocs.io/en/latest/examples.html#data-handling)
186
+ * [Up-Scaling](https://executorlib.readthedocs.io/en/latest/examples.html#up-scaling)
187
+ * [Coupled Functions](https://executorlib.readthedocs.io/en/latest/examples.html#coupled-functions)
188
+ * [SLURM Job Scheduler](https://executorlib.readthedocs.io/en/latest/examples.html#slurm-job-scheduler)
189
+ * [Workstation Support](https://executorlib.readthedocs.io/en/latest/examples.html#workstation-support)
190
+ * [Development](https://executorlib.readthedocs.io/en/latest/development.html)
191
+ * [Contributions](https://executorlib.readthedocs.io/en/latest/development.html#contributions)
192
+ * [License](https://executorlib.readthedocs.io/en/latest/development.html#license)
193
+ * [Integration](https://executorlib.readthedocs.io/en/latest/development.html#integration)
@@ -0,0 +1,127 @@
1
+ # executorlib
2
+ [![Unittests](https://github.com/pyiron/executorlib/actions/workflows/unittest-openmpi.yml/badge.svg)](https://github.com/pyiron/executorlib/actions/workflows/unittest-openmpi.yml)
3
+ [![Coverage Status](https://coveralls.io/repos/github/pyiron/executorlib/badge.svg?branch=main)](https://coveralls.io/github/pyiron/executorlib?branch=main)
4
+ [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/pyiron/executorlib/HEAD?labpath=notebooks%2Fexamples.ipynb)
5
+
6
+ ## Challenges
7
+ In high performance computing (HPC) the Python programming language is commonly used as high-level language to
8
+ orchestrate the coupling of scientific applications. Still the efficient usage of highly parallel HPC clusters remains
9
+ challenging, in primarily three aspects:
10
+
11
+ * **Communication**: Distributing python function calls over hundreds of compute node and gathering the results on a
12
+ shared file system is technically possible, but highly inefficient. A socket-based communication approach is
13
+ preferable.
14
+ * **Resource Management**: Assigning Python functions to GPUs or executing Python functions on multiple CPUs using the
15
+ message passing interface (MPI) requires major modifications to the python workflow.
16
+ * **Integration**: Existing workflow libraries implement a secondary the job management on the Python level rather than
17
+ leveraging the existing infrastructure provided by the job scheduler of the HPC.
18
+
19
+ ### executorlib is ...
20
+ In a given HPC allocation the `executorlib` library addresses these challenges by extending the Executor interface
21
+ of the standard Python library to support the resource assignment in the HPC context. Computing resources can either be
22
+ assigned on a per function call basis or as a block allocation on a per Executor basis. The `executorlib` library
23
+ is built on top of the [flux-framework](https://flux-framework.org) to enable fine-grained resource assignment. In
24
+ addition, [Simple Linux Utility for Resource Management (SLURM)](https://slurm.schedmd.com) is supported as alternative
25
+ queuing system and for workstation installations `executorlib` can be installed without a job scheduler.
26
+
27
+ ### executorlib is not ...
28
+ The executorlib library is not designed to request an allocation from the job scheduler of an HPC. Instead within a given
29
+ allocation from the job scheduler the `executorlib` library can be employed to distribute a series of python
30
+ function calls over the available computing resources to achieve maximum computing resource utilization.
31
+
32
+ ## Example
33
+ The following examples illustrates how `executorlib` can be used to distribute a series of MPI parallel function calls
34
+ within a queuing system allocation. `example.py`:
35
+ ```python
36
+ import flux.job
37
+ from executorlib import Executor
38
+
39
+ def calc(i):
40
+ from mpi4py import MPI
41
+ size = MPI.COMM_WORLD.Get_size()
42
+ rank = MPI.COMM_WORLD.Get_rank()
43
+ return i, size, rank
44
+
45
+ with flux.job.FluxExecutor() as flux_exe:
46
+ with Executor(max_cores=2, cores_per_worker=2, executor=flux_exe) as exe:
47
+ fs = exe.submit(calc, 3)
48
+ print(fs.result())
49
+ ```
50
+ This example can be executed using:
51
+ ```
52
+ python example.py
53
+ ```
54
+ Which returns:
55
+ ```
56
+ >>> [(0, 2, 0), (0, 2, 1)], [(1, 2, 0), (1, 2, 1)]
57
+ ```
58
+ The important part in this example is that [mpi4py](https://mpi4py.readthedocs.io) is only used in the `calc()`
59
+ function, not in the python script, consequently it is not necessary to call the script with `mpiexec` but instead
60
+ a call with the regular python interpreter is sufficient. This highlights how `executorlib` allows the users to
61
+ parallelize one function at a time and not having to convert their whole workflow to use [mpi4py](https://mpi4py.readthedocs.io).
62
+ The same code can also be executed inside a jupyter notebook directly which enables an interactive development process.
63
+
64
+ The interface of the standard [concurrent.futures.Executor](https://docs.python.org/3/library/concurrent.futures.html#module-concurrent.futures)
65
+ is extended by adding the option `cores_per_worker=2` to assign multiple MPI ranks to each function call. To create two
66
+ workers the maximum number of cores can be increased to `max_cores=4`. In this case each worker receives two cores
67
+ resulting in a total of four CPU cores being utilized.
68
+
69
+ After submitting the function `calc()` with the corresponding parameter to the executor `exe.submit(calc, 0)`
70
+ a python [`concurrent.futures.Future`](https://docs.python.org/3/library/concurrent.futures.html#future-objects) is
71
+ returned. Consequently, the `executorlib.Executor` can be used as a drop-in replacement for the
72
+ [`concurrent.futures.Executor`](https://docs.python.org/3/library/concurrent.futures.html#module-concurrent.futures)
73
+ which allows the user to add parallelism to their workflow one function at a time.
74
+
75
+ ## Disclaimer
76
+ While we try to develop a stable and reliable software library, the development remains a opensource project under the
77
+ BSD 3-Clause License without any warranties::
78
+ ```
79
+ BSD 3-Clause License
80
+
81
+ Copyright (c) 2022, Jan Janssen
82
+ All rights reserved.
83
+
84
+ Redistribution and use in source and binary forms, with or without
85
+ modification, are permitted provided that the following conditions are met:
86
+
87
+ * Redistributions of source code must retain the above copyright notice, this
88
+ list of conditions and the following disclaimer.
89
+
90
+ * Redistributions in binary form must reproduce the above copyright notice,
91
+ this list of conditions and the following disclaimer in the documentation
92
+ and/or other materials provided with the distribution.
93
+
94
+ * Neither the name of the copyright holder nor the names of its
95
+ contributors may be used to endorse or promote products derived from
96
+ this software without specific prior written permission.
97
+
98
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
99
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
100
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
101
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
102
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
103
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
104
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
105
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
106
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
107
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
108
+ ```
109
+
110
+ # Documentation
111
+ * [Installation](https://executorlib.readthedocs.io/en/latest/installation.html)
112
+ * [Compatible Job Schedulers](https://executorlib.readthedocs.io/en/latest/installation.html#compatible-job-schedulers)
113
+ * [executorlib with Flux Framework](https://executorlib.readthedocs.io/en/latest/installation.html#executorlib-with-flux-framework)
114
+ * [Test Flux Framework](https://executorlib.readthedocs.io/en/latest/installation.html#test-flux-framework)
115
+ * [Without Flux Framework](https://executorlib.readthedocs.io/en/latest/installation.html#without-flux-framework)
116
+ * [Examples](https://executorlib.readthedocs.io/en/latest/examples.html)
117
+ * [Compatibility](https://executorlib.readthedocs.io/en/latest/examples.html#compatibility)
118
+ * [Resource Assignment](https://executorlib.readthedocs.io/en/latest/examples.html#resource-assignment)
119
+ * [Data Handling](https://executorlib.readthedocs.io/en/latest/examples.html#data-handling)
120
+ * [Up-Scaling](https://executorlib.readthedocs.io/en/latest/examples.html#up-scaling)
121
+ * [Coupled Functions](https://executorlib.readthedocs.io/en/latest/examples.html#coupled-functions)
122
+ * [SLURM Job Scheduler](https://executorlib.readthedocs.io/en/latest/examples.html#slurm-job-scheduler)
123
+ * [Workstation Support](https://executorlib.readthedocs.io/en/latest/examples.html#workstation-support)
124
+ * [Development](https://executorlib.readthedocs.io/en/latest/development.html)
125
+ * [Contributions](https://executorlib.readthedocs.io/en/latest/development.html#contributions)
126
+ * [License](https://executorlib.readthedocs.io/en/latest/development.html#license)
127
+ * [Integration](https://executorlib.readthedocs.io/en/latest/development.html#integration)
@@ -0,0 +1,225 @@
1
+ from typing import Optional
2
+
3
+ from executorlib.interactive import create_executor
4
+ from executorlib.interactive.dependencies import ExecutorWithDependencies
5
+ from executorlib.shared.inputcheck import (
6
+ check_plot_dependency_graph as _check_plot_dependency_graph,
7
+ )
8
+ from executorlib.shared.inputcheck import (
9
+ check_refresh_rate as _check_refresh_rate,
10
+ )
11
+ from executorlib.shell.executor import SubprocessExecutor
12
+ from executorlib.shell.interactive import ShellExecutor
13
+
14
+ from ._version import get_versions
15
+
16
+ __version__ = get_versions()["version"]
17
+ __all__ = [
18
+ SubprocessExecutor,
19
+ ShellExecutor,
20
+ ]
21
+
22
+
23
+ try:
24
+ from executorlib.cache.executor import FileExecutor
25
+
26
+ __all__ += [FileExecutor]
27
+ except ImportError:
28
+ pass
29
+
30
+
31
+ class Executor:
32
+ """
33
+ The executorlib.Executor leverages either the message passing interface (MPI), the SLURM workload manager or
34
+ preferable the flux framework for distributing python functions within a given resource allocation. In contrast to
35
+ the mpi4py.futures.MPIPoolExecutor the executorlib.Executor can be executed in a serial python process and does not
36
+ require the python script to be executed with MPI. It is even possible to execute the executorlib.Executor directly
37
+ in an interactive Jupyter notebook.
38
+
39
+ Args:
40
+ max_workers (int): for backwards compatibility with the standard library, max_workers also defines the number of
41
+ cores which can be used in parallel - just like the max_cores parameter. Using max_cores is
42
+ recommended, as computers have a limited number of compute cores.
43
+ max_cores (int): defines the number cores which can be used in parallel
44
+ cores_per_worker (int): number of MPI cores to be used for each function call
45
+ threads_per_core (int): number of OpenMP threads to be used for each function call
46
+ gpus_per_worker (int): number of GPUs per worker - defaults to 0
47
+ oversubscribe (bool): adds the `--oversubscribe` command line flag (OpenMPI and SLURM only) - default False
48
+ cwd (str/None): current working directory where the parallel python task is executed
49
+ conda_environment_name (str): name of the conda environment to initialize
50
+ conda_environment_path (str): path of the conda environment to initialize
51
+ executor (flux.job.FluxExecutor): Flux Python interface to submit the workers to flux
52
+ hostname_localhost (boolean): use localhost instead of the hostname to establish the zmq connection. In the
53
+ context of an HPC cluster this essential to be able to communicate to an
54
+ Executor running on a different compute node within the same allocation. And
55
+ in principle any computer should be able to resolve that their own hostname
56
+ points to the same address as localhost. Still MacOS >= 12 seems to disable
57
+ this look up for security reasons. So on MacOS it is required to set this
58
+ option to true
59
+ backend (str): Switch between the different backends "flux", "local" or "slurm". Alternatively, when "auto"
60
+ is selected (the default) the available backend is determined automatically.
61
+ block_allocation (boolean): To accelerate the submission of a series of python functions with the same resource
62
+ requirements, executorlib supports block allocation. In this case all resources have
63
+ to be defined on the executor, rather than during the submission of the individual
64
+ function.
65
+ init_function (None): optional function to preset arguments for functions which are submitted later
66
+ command_line_argument_lst (list): Additional command line arguments for the srun call (SLURM only)
67
+ pmi (str): PMI interface to use (OpenMPI v5 requires pmix) default is None (Flux only)
68
+ disable_dependencies (boolean): Disable resolving future objects during the submission.
69
+ refresh_rate (float): Set the refresh rate in seconds, how frequently the input queue is checked.
70
+ plot_dependency_graph (bool): Plot the dependencies of multiple future objects without executing them. For
71
+ debugging purposes and to get an overview of the specified dependencies.
72
+
73
+ Examples:
74
+ ```
75
+ >>> import numpy as np
76
+ >>> from executorlib import Executor
77
+ >>>
78
+ >>> def calc(i, j, k):
79
+ >>> from mpi4py import MPI
80
+ >>> size = MPI.COMM_WORLD.Get_size()
81
+ >>> rank = MPI.COMM_WORLD.Get_rank()
82
+ >>> return np.array([i, j, k]), size, rank
83
+ >>>
84
+ >>> def init_k():
85
+ >>> return {"k": 3}
86
+ >>>
87
+ >>> with Executor(cores=2, init_function=init_k) as p:
88
+ >>> fs = p.submit(calc, 2, j=4)
89
+ >>> print(fs.result())
90
+ [(array([2, 4, 3]), 2, 0), (array([2, 4, 3]), 2, 1)]
91
+ ```
92
+ """
93
+
94
+ def __init__(
95
+ self,
96
+ max_workers: int = 1,
97
+ max_cores: int = 1,
98
+ cores_per_worker: int = 1,
99
+ threads_per_core: int = 1,
100
+ gpus_per_worker: int = 0,
101
+ oversubscribe: bool = False,
102
+ cwd: Optional[str] = None,
103
+ conda_environment_name: Optional[str] = None,
104
+ conda_environment_path: Optional[str] = None,
105
+ executor=None,
106
+ hostname_localhost: bool = False,
107
+ backend: str = "auto",
108
+ block_allocation: bool = True,
109
+ init_function: Optional[callable] = None,
110
+ command_line_argument_lst: list[str] = [],
111
+ pmi: Optional[str] = None,
112
+ disable_dependencies: bool = False,
113
+ refresh_rate: float = 0.01,
114
+ plot_dependency_graph: bool = False,
115
+ ):
116
+ # Use __new__() instead of __init__(). This function is only implemented to enable auto-completion.
117
+ pass
118
+
119
+ def __new__(
120
+ cls,
121
+ max_workers: int = 1,
122
+ max_cores: int = 1,
123
+ cores_per_worker: int = 1,
124
+ threads_per_core: int = 1,
125
+ gpus_per_worker: int = 0,
126
+ oversubscribe: bool = False,
127
+ cwd: Optional[str] = None,
128
+ conda_environment_name: Optional[str] = None,
129
+ conda_environment_path: Optional[str] = None,
130
+ executor=None,
131
+ hostname_localhost: bool = False,
132
+ backend: str = "auto",
133
+ block_allocation: bool = False,
134
+ init_function: Optional[callable] = None,
135
+ command_line_argument_lst: list[str] = [],
136
+ pmi: Optional[str] = None,
137
+ disable_dependencies: bool = False,
138
+ refresh_rate: float = 0.01,
139
+ plot_dependency_graph: bool = False,
140
+ ):
141
+ """
142
+ Instead of returning a executorlib.Executor object this function returns either a executorlib.mpi.PyMPIExecutor,
143
+ executorlib.slurm.PySlurmExecutor or executorlib.flux.PyFluxExecutor depending on which backend is available. The
144
+ executorlib.flux.PyFluxExecutor is the preferred choice while the executorlib.mpi.PyMPIExecutor is primarily used
145
+ for development and testing. The executorlib.flux.PyFluxExecutor requires flux-core from the flux-framework to be
146
+ installed and in addition flux-sched to enable GPU scheduling. Finally, the executorlib.slurm.PySlurmExecutor
147
+ requires the SLURM workload manager to be installed on the system.
148
+
149
+ Args:
150
+ max_workers (int): for backwards compatibility with the standard library, max_workers also defines the
151
+ number of cores which can be used in parallel - just like the max_cores parameter. Using
152
+ max_cores is recommended, as computers have a limited number of compute cores.
153
+ max_cores (int): defines the number cores which can be used in parallel
154
+ cores_per_worker (int): number of MPI cores to be used for each function call
155
+ threads_per_core (int): number of OpenMP threads to be used for each function call
156
+ gpus_per_worker (int): number of GPUs per worker - defaults to 0
157
+ oversubscribe (bool): adds the `--oversubscribe` command line flag (OpenMPI and SLURM only) - default False
158
+ cwd (str/None): current working directory where the parallel python task is executed
159
+ conda_environment_name (str): name of the conda environment to initialize
160
+ conda_environment_path (str): path of the conda environment to initialize
161
+ executor (flux.job.FluxExecutor): Flux Python interface to submit the workers to flux
162
+ hostname_localhost (boolean): use localhost instead of the hostname to establish the zmq connection. In the
163
+ context of an HPC cluster this essential to be able to communicate to an
164
+ Executor running on a different compute node within the same allocation. And
165
+ in principle any computer should be able to resolve that their own hostname
166
+ points to the same address as localhost. Still MacOS >= 12 seems to disable
167
+ this look up for security reasons. So on MacOS it is required to set this
168
+ option to true
169
+ backend (str): Switch between the different backends "flux", "local" or "slurm". Alternatively, when "auto"
170
+ is selected (the default) the available backend is determined automatically.
171
+ block_allocation (boolean): To accelerate the submission of a series of python functions with the same
172
+ resource requirements, executorlib supports block allocation. In this case all
173
+ resources have to be defined on the executor, rather than during the submission
174
+ of the individual function.
175
+ init_function (None): optional function to preset arguments for functions which are submitted later
176
+ command_line_argument_lst (list): Additional command line arguments for the srun call (SLURM only)
177
+ pmi (str): PMI interface to use (OpenMPI v5 requires pmix) default is None (Flux only)
178
+ disable_dependencies (boolean): Disable resolving future objects during the submission.
179
+ refresh_rate (float): Set the refresh rate in seconds, how frequently the input queue is checked.
180
+ plot_dependency_graph (bool): Plot the dependencies of multiple future objects without executing them. For
181
+ debugging purposes and to get an overview of the specified dependencies.
182
+
183
+ """
184
+ if not disable_dependencies:
185
+ return ExecutorWithDependencies(
186
+ max_workers=max_workers,
187
+ max_cores=max_cores,
188
+ cores_per_worker=cores_per_worker,
189
+ threads_per_core=threads_per_core,
190
+ gpus_per_worker=gpus_per_worker,
191
+ oversubscribe=oversubscribe,
192
+ cwd=cwd,
193
+ conda_environment_name=conda_environment_name,
194
+ conda_environment_path=conda_environment_path,
195
+ executor=executor,
196
+ hostname_localhost=hostname_localhost,
197
+ backend=backend,
198
+ block_allocation=block_allocation,
199
+ init_function=init_function,
200
+ command_line_argument_lst=command_line_argument_lst,
201
+ pmi=pmi,
202
+ refresh_rate=refresh_rate,
203
+ plot_dependency_graph=plot_dependency_graph,
204
+ )
205
+ else:
206
+ _check_plot_dependency_graph(plot_dependency_graph=plot_dependency_graph)
207
+ _check_refresh_rate(refresh_rate=refresh_rate)
208
+ return create_executor(
209
+ max_workers=max_workers,
210
+ max_cores=max_cores,
211
+ cores_per_worker=cores_per_worker,
212
+ threads_per_core=threads_per_core,
213
+ gpus_per_worker=gpus_per_worker,
214
+ oversubscribe=oversubscribe,
215
+ cwd=cwd,
216
+ conda_environment_name=conda_environment_name,
217
+ conda_environment_path=conda_environment_path,
218
+ executor=executor,
219
+ hostname_localhost=hostname_localhost,
220
+ backend=backend,
221
+ block_allocation=block_allocation,
222
+ init_function=init_function,
223
+ command_line_argument_lst=command_line_argument_lst,
224
+ pmi=pmi,
225
+ )
@@ -0,0 +1,21 @@
1
+
2
+ # This file was generated by 'versioneer.py' (0.29) from
3
+ # revision-control system data, or from the parent directory name of an
4
+ # unpacked source archive. Distribution tarballs contain a pre-generated copy
5
+ # of this file.
6
+
7
+ import json
8
+
9
+ version_json = '''
10
+ {
11
+ "date": "2024-07-14T21:06:50+0200",
12
+ "dirty": true,
13
+ "error": null,
14
+ "full-revisionid": "0a84450d76b7081b62a9d948bac56f905d013b5d",
15
+ "version": "0.0.1"
16
+ }
17
+ ''' # END VERSION_JSON
18
+
19
+
20
+ def get_versions():
21
+ return json.loads(version_json)
File without changes
@@ -0,0 +1,40 @@
1
+ import pickle
2
+ import sys
3
+
4
+ import cloudpickle
5
+
6
+ from executorlib.cache.shared import backend_load_file, backend_write_file
7
+
8
+
9
+ def main():
10
+ from mpi4py import MPI
11
+
12
+ MPI.pickle.__init__(
13
+ cloudpickle.dumps,
14
+ cloudpickle.loads,
15
+ pickle.HIGHEST_PROTOCOL,
16
+ )
17
+ mpi_rank_zero = MPI.COMM_WORLD.Get_rank() == 0
18
+ mpi_size_larger_one = MPI.COMM_WORLD.Get_size() > 1
19
+ file_name = sys.argv[1]
20
+
21
+ if mpi_rank_zero:
22
+ apply_dict = backend_load_file(file_name=file_name)
23
+ else:
24
+ apply_dict = None
25
+ apply_dict = MPI.COMM_WORLD.bcast(apply_dict, root=0)
26
+ output = apply_dict["fn"].__call__(*apply_dict["args"], **apply_dict["kwargs"])
27
+ if mpi_size_larger_one:
28
+ result = MPI.COMM_WORLD.gather(output, root=0)
29
+ else:
30
+ result = output
31
+ if mpi_rank_zero:
32
+ backend_write_file(
33
+ file_name=file_name,
34
+ output=result,
35
+ )
36
+ MPI.COMM_WORLD.Barrier()
37
+
38
+
39
+ if __name__ == "__main__":
40
+ main()
@@ -0,0 +1,6 @@
1
+ import sys
2
+
3
+ from executorlib.cache.shared import execute_task_in_file
4
+
5
+ if __name__ == "__main__":
6
+ execute_task_in_file(file_name=sys.argv[1])