Multiclass-interface 1.14__tar.gz → 1.15__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 (24) hide show
  1. {multiclass_interface-1.14 → multiclass_interface-1.15}/.gitlab-ci.yml +3 -22
  2. {multiclass_interface-1.14 → multiclass_interface-1.15/Multiclass_interface.egg-info}/PKG-INFO +1 -1
  3. {multiclass_interface-1.14/Multiclass_interface.egg-info → multiclass_interface-1.15}/PKG-INFO +1 -1
  4. {multiclass_interface-1.14 → multiclass_interface-1.15}/multiclass_interface/mpi_interface.py +63 -45
  5. multiclass_interface-1.15/multiclass_interface/tests/test_mpi.py +211 -0
  6. {multiclass_interface-1.14 → multiclass_interface-1.15}/multiclass_interface/version.py +9 -4
  7. {multiclass_interface-1.14 → multiclass_interface-1.15}/pyproject.toml +0 -3
  8. multiclass_interface-1.14/multiclass_interface/tests/test_mpi.py +0 -99
  9. {multiclass_interface-1.14 → multiclass_interface-1.15}/.gitignore +0 -0
  10. {multiclass_interface-1.14 → multiclass_interface-1.15}/LICENSE +0 -0
  11. {multiclass_interface-1.14 → multiclass_interface-1.15}/Multiclass_interface.egg-info/SOURCES.txt +0 -0
  12. {multiclass_interface-1.14 → multiclass_interface-1.15}/Multiclass_interface.egg-info/dependency_links.txt +0 -0
  13. {multiclass_interface-1.14 → multiclass_interface-1.15}/Multiclass_interface.egg-info/requires.txt +0 -0
  14. {multiclass_interface-1.14 → multiclass_interface-1.15}/Multiclass_interface.egg-info/top_level.txt +0 -0
  15. {multiclass_interface-1.14 → multiclass_interface-1.15}/README.md +0 -0
  16. {multiclass_interface-1.14 → multiclass_interface-1.15}/multiclass_interface/__init__.py +0 -0
  17. {multiclass_interface-1.14 → multiclass_interface-1.15}/multiclass_interface/multi_object_list.py +0 -0
  18. {multiclass_interface-1.14 → multiclass_interface-1.15}/multiclass_interface/multiprocess_interface.py +0 -0
  19. {multiclass_interface-1.14 → multiclass_interface-1.15}/multiclass_interface/tests/__init__.py +0 -0
  20. {multiclass_interface-1.14 → multiclass_interface-1.15}/multiclass_interface/tests/my_test_cls.py +0 -0
  21. {multiclass_interface-1.14 → multiclass_interface-1.15}/multiclass_interface/tests/test_multiclass_list.py +0 -0
  22. {multiclass_interface-1.14 → multiclass_interface-1.15}/multiclass_interface/tests/test_multiprocessinterface.py +0 -0
  23. {multiclass_interface-1.14 → multiclass_interface-1.15}/sequence_diagrams.md +0 -0
  24. {multiclass_interface-1.14 → multiclass_interface-1.15}/setup.cfg +0 -0
@@ -2,39 +2,22 @@ image: continuumio/anaconda3
2
2
 
3
3
  stages:
4
4
  - test
5
- - build
6
5
  - deploy
7
6
 
8
- # ===== TEST Dynamiks ubuntu =====
9
- test_ubuntu: # name the job what we like
10
- image: continuumio/anaconda3
11
- stage: # build, test, deploy defined by default [2]
12
- test
13
- script:
14
- - pip install -e .[test]
15
- - pytest --cov-report term-missing:skip-covered --cov=multiclass_interface multiclass_interface/tests
16
- coverage: '/(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/'
17
- tags: # only runners with this tag can do the job [3]
18
- - ci-ubuntu
19
- rules:
20
- - if: $CI_COMMIT_BRANCH == "main"
21
- - if: $CI_PIPELINE_SOURCE == "merge_request_event"
22
-
23
-
24
- mpitest-linux_openmpi:
7
+ test_mpitest_openmpi:
25
8
  image: registry.windenergy.dtu.dk/hawc2/hawc2lib:mpi
26
9
  variables:
27
10
  GIT_SUBMODULE_STRATEGY: recursive # need to checkout hawc2 and utils
28
11
  script:
29
12
  - pip install .[test]
30
- - mpirun -n 4 python -c "from multiclass_interface.tests.test_mpi import mpitest_all; mpitest_all()"
31
- - python -c "from multiclass_interface import version; print (f'{version.__version__}')"
13
+ - pytest --cov-report term-missing:skip-covered --cov=multiclass_interface multiclass_interface/tests
32
14
  rules:
33
15
  - if: $CI_COMMIT_BRANCH == "main"
34
16
  - if: $CI_PIPELINE_SOURCE == "merge_request_event"
35
17
  tags:
36
18
  - ci-ubuntu
37
19
 
20
+
38
21
  # ===== Check code style =====
39
22
  check_code_style: # name the job what we like
40
23
  stage: # build, test, deploy defined by default [2]
@@ -49,8 +32,6 @@ check_code_style: # name the job what we like
49
32
  - if: $CI_PIPELINE_SOURCE == "merge_request_event"
50
33
 
51
34
 
52
-
53
-
54
35
  pypi_linux:
55
36
  stage:
56
37
  deploy
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: Multiclass_interface
3
- Version: 1.14
3
+ Version: 1.15
4
4
  Summary: Multiclass interface
5
5
  Author-email: "Mads M. Pedersen" <mmpe@dtu.dk>
6
6
  Project-URL: Homepage, https://gitlab.windenergy.dtu.dk/DYNAMIKS/multiclass_interface
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: Multiclass_interface
3
- Version: 1.14
3
+ Version: 1.15
4
4
  Summary: Multiclass interface
5
5
  Author-email: "Mads M. Pedersen" <mmpe@dtu.dk>
6
6
  Project-URL: Homepage, https://gitlab.windenergy.dtu.dk/DYNAMIKS/multiclass_interface
@@ -1,7 +1,7 @@
1
1
  import numpy as np
2
2
  import sys
3
3
  import traceback
4
- import inspect
4
+ from contextlib import contextmanager
5
5
 
6
6
  comm = None
7
7
  size = 1
@@ -11,8 +11,8 @@ mpi = False
11
11
  main = True
12
12
 
13
13
 
14
- def activate_mpi(collective_mpi=True, loop_until_close=True, terminate_on_close=True):
15
- global comm, size, rank, name, main, mpi, LOOP_UNTIL_CLOSE, TERMINATE_ON_CLOSE, COLLECTIVE_MPI
14
+ def activate_mpi(collective_mpi=True, terminate_on_close=True):
15
+ global comm, size, rank, name, main, mpi, TERMINATE_ON_CLOSE, COLLECTIVE_MPI
16
16
  from mpi4py import MPI
17
17
  comm = MPI.COMM_WORLD
18
18
  size = MPI.COMM_WORLD.Get_size()
@@ -21,12 +21,12 @@ def activate_mpi(collective_mpi=True, loop_until_close=True, terminate_on_close=
21
21
  mpi = True
22
22
  main = rank == 0
23
23
  COLLECTIVE_MPI = collective_mpi
24
- LOOP_UNTIL_CLOSE = loop_until_close
25
24
  TERMINATE_ON_CLOSE = terminate_on_close
26
25
 
27
26
 
28
27
  MPIClassInterfaceAttributes = {
29
28
  'close',
29
+ 'release_mpi_workers',
30
30
  'use_rank',
31
31
  'cls',
32
32
  'work_loop',
@@ -38,7 +38,6 @@ MPIClassInterfaceAttributes = {
38
38
  'closed'}
39
39
 
40
40
 
41
- LOOP_UNTIL_CLOSE = True # MPI workers (rank>0) will enter the work loop and wait for commands from rank 0
42
41
  TERMINATE_ON_CLOSE = True # MPI workers (rank>0) will terminate (sys.exit) when the close command is called via rank0
43
42
  COLLECTIVE_MPI = True # rank0 acts as a single point of contact (scatters command+arg and gathers results)
44
43
 
@@ -59,6 +58,7 @@ def main_run(f, *args, **kwargs):
59
58
 
60
59
  class MPIClassInterface():
61
60
  def __init__(self, cls, args_lst):
61
+ self.closed = False
62
62
  if len(args_lst) > size:
63
63
  if rank == 0:
64
64
  raise Exception(f"Not enough mpi slots. Slots: {size}, Requested: {len(args_lst)}")
@@ -73,13 +73,12 @@ class MPIClassInterface():
73
73
 
74
74
  def __getattr__(self, name):
75
75
  raise AttributeError(
76
- f"rank {rank} trying to access {name}, but only {len(args_lst)} instances of {cls.__name__} exists")
76
+ f"Rank {rank} trying to access {name}, but only {len(args_lst)} instances of {cls.__name__} exists")
77
77
  self.object = Dummy()
78
- self.closed = False
79
78
 
80
79
  self.use_rank = np.array([True] * size)
81
80
  self.use_rank[len(args_lst):] = False
82
- if rank > 0 and COLLECTIVE_MPI and LOOP_UNTIL_CLOSE:
81
+ if rank > 0 and COLLECTIVE_MPI:
83
82
  self.work_loop()
84
83
 
85
84
  def __enter__(self):
@@ -92,20 +91,20 @@ class MPIClassInterface():
92
91
  while True:
93
92
  method, args, kwargs = comm.scatter(None)
94
93
  comm.gather(self.do_task(method, args, kwargs))
95
- if LOOP_UNTIL_CLOSE:
96
- if method == 'close':
97
- if TERMINATE_ON_CLOSE:
98
- # comm.gather(f'Exit, rank {rank}')
99
- print("sys.exit", rank, flush=True)
100
- sys.exit(0)
101
- else:
102
- raise ChildProcessError('MPI worker done')
103
- else:
94
+ if method == 'exit_work_loop':
104
95
  break
96
+ if method == 'close':
97
+ if TERMINATE_ON_CLOSE:
98
+ # comm.gather(f'Exit, rank {rank}')
99
+ print("sys.exit", rank, flush=True)
100
+ self.closed = True
101
+ sys.exit(0)
102
+ else:
103
+ raise ChildProcessError('MPI worker done')
105
104
 
106
105
  def do_task(self, method, args, kwargs):
107
106
  try:
108
- if method == 'skip':
107
+ if method == 'skip' or method == 'exit_work_loop':
109
108
  res = None
110
109
  elif method == 'setattr':
111
110
  name, value = args
@@ -119,29 +118,28 @@ class MPIClassInterface():
119
118
  return res
120
119
 
121
120
  def run_task(self, input_lst):
121
+ assert rank == 0, "other ranks should be in work_loop when invoking run_task. For non-collective mpi, do_task should be used"
122
122
  comm.scatter(input_lst, root=0)
123
- if rank == 0:
124
- method, args, kwargs = input_lst[0]
125
- res = self.do_task(method, args, kwargs)
126
- else:
127
- res = None
123
+ method, args, kwargs = input_lst[0]
124
+ res = self.do_task(method, args, kwargs)
125
+
128
126
  use_rank = self.use_rank
129
127
 
130
128
  res = [res for i, res in enumerate(comm.gather(res, root=0)) if use_rank[i]]
131
- if rank == 0:
132
- for r in res:
133
- if isinstance(r, tuple) and len(r) > 1 and isinstance(r[0], BaseException):
134
- raise r[0].__class__(r[1])
129
+ for r in res:
130
+ if isinstance(r, tuple) and len(r) > 1 and isinstance(r[0], BaseException):
131
+ raise r[0].__class__(r[1])
135
132
  return res
136
133
 
137
134
  def get_input(self, name, i, args, kwargs):
138
135
  use_rank = self.use_rank
139
- if COLLECTIVE_MPI:
140
- N = np.sum(use_rank)
141
- j = sum(use_rank[:i])
142
- else:
143
- N = 1
144
- j = 0
136
+ N = np.sum(use_rank)
137
+ j = sum(use_rank[:i]) # needed for subsets
138
+
139
+ # if COLLECTIVE_MPI:
140
+ # j = sum(use_rank[:i])
141
+ # else:
142
+ # j = 0
145
143
 
146
144
  if use_rank[i]:
147
145
  def get_arg(arg):
@@ -149,6 +147,7 @@ class MPIClassInterface():
149
147
  return arg[j]
150
148
  else:
151
149
  return arg
150
+
152
151
  return (name, [get_arg(arg) for arg in args], {k: get_arg(v) for k, v in kwargs.items()})
153
152
  else:
154
153
  return ('skip', [], {})
@@ -156,10 +155,7 @@ class MPIClassInterface():
156
155
  def __getattribute__(self, name):
157
156
  if name in MPIClassInterfaceAttributes:
158
157
  return object.__getattribute__(self, name)
159
- elif COLLECTIVE_MPI and rank > 0:
160
- self.work_loop()
161
- return lambda *args, **kwargs: 1
162
- elif rank > 0 and not self.use_rank[rank]:
158
+ elif not self.use_rank[rank]:
163
159
  raise ChildProcessError(f'MPI worker {rank} not used')
164
160
 
165
161
  def wrap(*args, **kwargs):
@@ -167,20 +163,28 @@ class MPIClassInterface():
167
163
  inp = [self.get_input(name, i, args, kwargs) for i in range(size)]
168
164
  return self.run_task(inp)
169
165
  else:
170
- method, args, kwargs = self.get_input(name, 0, args, kwargs)
171
- return [self.do_task(name, args, kwargs)]
172
- if hasattr(getattr(self.object, name), '__call__'):
166
+ method, args, kwargs = self.get_input(name, rank, args, kwargs)
167
+ return [self.do_task(method, args, kwargs)]
168
+ try:
169
+ att = getattr(self.object, name)
170
+ except AttributeError as e:
171
+ raise type(e)(f'Rank {rank}: {e}').with_traceback(sys.exc_info()[2])
172
+
173
+ if hasattr(att, '__call__'):
173
174
  res = wrap
174
175
  else:
175
176
  res = wrap()
176
177
  return res
177
178
 
178
179
  def __setattr__(self, name, value):
179
- if rank > 0 or name in MPIClassInterfaceAttributes:
180
+ if (COLLECTIVE_MPI and rank > 0) or name in MPIClassInterfaceAttributes:
180
181
  return object.__setattr__(self, name, value)
181
182
  else:
182
183
  inp = [self.get_input('setattr', i, (name, value), {}) for i in range(size)]
183
- return self.run_task(inp)
184
+ if COLLECTIVE_MPI:
185
+ return self.run_task(inp)
186
+ else:
187
+ return self.do_task(*inp[rank])
184
188
 
185
189
  def __getitem__(self, slice):
186
190
  use_rank = np.full_like(self.use_rank, False)
@@ -189,14 +193,28 @@ class MPIClassInterface():
189
193
  return self
190
194
  return SubsetMPIClassInterface(self.cls, self.object, use_rank)
191
195
 
196
+ @contextmanager
197
+ def release_mpi_workers(self):
198
+ global COLLECTIVE_MPI
199
+ """Use to release mpi_workers. All object statements including 'release_mpi_workers' must be protected by 'if mpi_interface.main:' """
200
+ if COLLECTIVE_MPI:
201
+ if rank == 0:
202
+ self.run_task([('exit_work_loop', [], {}) for _ in range(size)])
203
+ COLLECTIVE_MPI = False
204
+ try:
205
+ yield self
206
+ finally:
207
+ COLLECTIVE_MPI = True
208
+ if rank > 0 and not self.closed:
209
+ self.work_loop()
210
+
192
211
  def close(self):
212
+
193
213
  if not self.closed:
214
+
194
215
  if COLLECTIVE_MPI:
195
216
  if rank == 0:
196
217
  res = self.run_task([('close', [], {}) for _ in range(size)])
197
- else:
198
- self.work_loop()
199
- res = None
200
218
  else:
201
219
  res = self.do_task('close', [], {})
202
220
  self.closed = True
@@ -0,0 +1,211 @@
1
+ import os
2
+ import sys
3
+ import time
4
+ import traceback
5
+ from numpy import testing as npt
6
+ import pytest
7
+
8
+ from multiclass_interface.mpi_interface import MPIClassInterface
9
+ from multiclass_interface.multiprocess_interface import ProcessClass
10
+ import numpy as np
11
+ from multiclass_interface.tests.my_test_cls import MyTest
12
+ from multiclass_interface import mpi_interface
13
+
14
+
15
+ def mpirun(f):
16
+ if 'main()' in traceback.format_stack()[0]:
17
+ n = f.__name__
18
+
19
+ def wrap():
20
+ exe = sys.executable.replace("\\", "/")
21
+ cmd = f'''mpirun -n 4 {exe} -c "from multiclass_interface.tests.test_mpi import {n}; {n}()"'''
22
+ # print(cmd)
23
+ assert os.system(cmd) == 0, n
24
+ wrap.__name__ = n
25
+ return wrap
26
+ else:
27
+ return f
28
+
29
+
30
+ @mpirun
31
+ def test_mpi_collective_too_few_slots():
32
+ mpi_interface.activate_mpi()
33
+ N = 4
34
+ rank = mpi_interface.rank
35
+
36
+ try:
37
+ with MPIClassInterface(MyTest, [(i + 1,) for i in range(N + 1)]) as m:
38
+ pass
39
+ assert rank in [1, 2, 3]
40
+ except Exception as e:
41
+ assert rank == 0
42
+ assert str(e) == 'Not enough mpi slots. Slots: 4, Requested: 5'
43
+
44
+
45
+ @mpirun
46
+ def test_mpi_collective_MyTest():
47
+ mpi_interface.activate_mpi()
48
+ N = 4
49
+ rank = mpi_interface.rank
50
+
51
+ with MPIClassInterface(MyTest, [(i + 1,) for i in range(N)]) as m:
52
+ if mpi_interface.main:
53
+ i = m.get_id()
54
+ npt.assert_array_equal(i, np.arange(N) + 1)
55
+
56
+ with m.release_mpi_workers():
57
+ assert m.get_id() == [rank + 1]
58
+
59
+ assert m.get_id() == [1, 2, 3, 4]
60
+ i = mpi_interface.main_run(lambda: m.get_id())
61
+ npt.assert_array_equal(i, 1)
62
+
63
+ try:
64
+ npt.assert_array_equal(m[1:3].get_id(), np.arange(1, 3) + 1)
65
+ except ChildProcessError:
66
+ pass
67
+
68
+ m.x = "hello"
69
+ assert m.x == ['hello'] * 4
70
+
71
+ assert m.return_input([101, 102, 103, 104])[0] == f'1 got: ({101},) and ' + '{}'
72
+ m.x = 'hello'
73
+ assert m.x == ['hello'] * 4
74
+ m[1::2].x = 'world'
75
+ assert m[:].x == ['hello', 'world', 'hello', 'world']
76
+
77
+ m.release_mpi_workers()
78
+ m.x = [1, 2, 3, 4]
79
+ assert m.x == [1, 2, 3, 4]
80
+ m.y = list(np.array(m.x) + 10)
81
+ assert m.y == [11, 12, 13, 14]
82
+ t = time.time()
83
+ m.work(1)
84
+ t = time.time() - t
85
+ assert t < 1.1
86
+
87
+ with pytest.raises(Exception, match='Cannot close SubsetMPIClassInterface. Please close all instances at once'):
88
+ m[:3].close()
89
+ with pytest.raises(Exception, match='Cannot make subset of SubsetMPIClassInterface'):
90
+ m[:3][1]
91
+
92
+
93
+ @mpirun
94
+ def test_mpi_collective_close_released_mpi_workers():
95
+ mpi_interface.activate_mpi()
96
+ N = 4
97
+ rank = mpi_interface.rank
98
+ with MPIClassInterface(MyTest, [(i + 1,) for i in range(N)]) as m:
99
+ with m.release_mpi_workers():
100
+ m.close()
101
+
102
+
103
+ @mpirun
104
+ def test_mpi_ProcessClass():
105
+ mpi_interface.activate_mpi()
106
+ with ProcessClass(MyTest) as cls:
107
+ myTest = cls(1)
108
+ assert myTest.get_id() == 1
109
+
110
+
111
+ @mpirun
112
+ def test_non_collective_mpi2():
113
+ mpi_interface.activate_mpi(collective_mpi=False)
114
+ rank = mpi_interface.rank
115
+ N = 3
116
+ with MPIClassInterface(MyTest, [(i + 10,) for i in range(N)]) as m:
117
+
118
+ try:
119
+ assert m.get_id()[0] == rank + 10, m.get_id()
120
+ main_id = mpi_interface.main_run(lambda: m.get_id())
121
+ assert main_id == [10], (rank, main_id)
122
+ assert rank < N # rank > N will fail with ChildProcessError
123
+ assert m.return_input([101, 102, 103])[0] == f'{rank + 10} got: ({rank + 101},) and ' + '{}'
124
+ m.x = 'hello'
125
+ assert m.x[0] == 'hello', m.x[0]
126
+ m.x = [1, 2, 3]
127
+ assert m.x[0] == rank + 1
128
+ m.y = m.x[0] + 10
129
+ assert m.y[0] == rank + 11
130
+ except ChildProcessError:
131
+ pass
132
+
133
+ if rank == 3:
134
+ with pytest.raises(AttributeError, match='Rank 3 trying to access cls, but only 3 instances of MyTest exists'):
135
+ m.object.cls
136
+
137
+
138
+ @mpirun
139
+ def test_mpi_non_collective_MyTest():
140
+ mpi_interface.activate_mpi(collective_mpi=False)
141
+ rank = mpi_interface.rank
142
+ N = 4
143
+
144
+ with MPIClassInterface(MyTest, [(i + 1,) for i in range(N)]) as m:
145
+ i = m.get_id()[0]
146
+ assert i == rank + 1
147
+ i = mpi_interface.main_run(lambda: m.get_id())
148
+ npt.assert_array_equal(i, 1) # id of rank0 broadcast to all
149
+ try:
150
+ assert m[1:3].get_id()[0] == rank + 1
151
+ except ChildProcessError:
152
+ assert rank in [0, 3]
153
+
154
+ # rank>0 fails, but only rank=0 is needed, so should not fail
155
+ mpi_interface.main_run(lambda rank=rank: 1 / [0, 1][rank == 0])
156
+
157
+ assert m.return_input([101, 102, 103, 104])[0] == f'{rank + 1} got: ({rank + 101},) and ' + '{}'
158
+ m.x = "hello"
159
+ assert m.x[0] == 'hello'
160
+
161
+ m[1::2].x = 'world'
162
+ assert m.x[0] == ['hello', 'world'][rank % 2]
163
+
164
+ try:
165
+ assert m[1::2].x[0] == 'world'
166
+ assert m[1::2][:].x[0] == 'world'
167
+ except ChildProcessError:
168
+ pass
169
+
170
+ m.x = [1, 2, 3, 4]
171
+ assert m.x[0] == rank + 1
172
+
173
+ m.y = m.x[0] + 10
174
+ assert m.y[0] == rank + 11
175
+ t = time.time()
176
+ m.work(1)
177
+ t = time.time() - t
178
+ assert t < 1.1
179
+
180
+ with pytest.raises(Exception, match='Cannot close SubsetMPIClassInterface. Please close all instances at once'):
181
+ m[:3].close()
182
+ with pytest.raises(Exception, match='Cannot make subset of SubsetMPIClassInterface'):
183
+ m[:3][1]
184
+
185
+
186
+ @mpirun
187
+ def test_multiprocessinterface_tests():
188
+ from multiclass_interface.tests.test_multiprocessinterface import test_attribute, test_missing_attribute, test_execption, test_setattr, test_setattr_method
189
+ mpi_interface.activate_mpi()
190
+
191
+ try:
192
+ with MPIClassInterface(MyTest, [(1,), (2,), (3,)]) as mpici:
193
+ for f in [test_attribute, test_missing_attribute, test_execption, test_setattr, test_setattr_method]:
194
+ f(mpici)
195
+ except ChildProcessError:
196
+ pass
197
+
198
+
199
+ @mpirun
200
+ def test_multiprocessinterface_tests_diff_interface():
201
+ from multiclass_interface.tests.test_multiprocessinterface import test_attribute, test_missing_attribute, test_execption, test_setattr, test_setattr_method
202
+ mpi_interface.activate_mpi(terminate_on_close=False)
203
+
204
+ for f in [test_attribute, test_missing_attribute, test_execption, test_setattr, test_setattr_method]:
205
+ # print (mpi_interface.rank, f.__name__,flush=True)
206
+ try:
207
+ with MPIClassInterface(MyTest, [(1,), (2,), (3,)]) as mpici:
208
+ f(mpici)
209
+ except ChildProcessError:
210
+ pass
211
+ sys.exit(0)
@@ -1,8 +1,13 @@
1
- # file generated by setuptools_scm
1
+ # file generated by setuptools-scm
2
2
  # don't change, don't track in version control
3
+
4
+ __all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
5
+
3
6
  TYPE_CHECKING = False
4
7
  if TYPE_CHECKING:
5
- from typing import Tuple, Union
8
+ from typing import Tuple
9
+ from typing import Union
10
+
6
11
  VERSION_TUPLE = Tuple[Union[int, str], ...]
7
12
  else:
8
13
  VERSION_TUPLE = object
@@ -12,5 +17,5 @@ __version__: str
12
17
  __version_tuple__: VERSION_TUPLE
13
18
  version_tuple: VERSION_TUPLE
14
19
 
15
- __version__ = version = '1.14'
16
- __version_tuple__ = version_tuple = (1, 14)
20
+ __version__ = version = '1.15'
21
+ __version_tuple__ = version_tuple = (1, 15)
@@ -41,9 +41,6 @@ version_file = "multiclass_interface/version.py"
41
41
  [tool.coverage.run]
42
42
  omit = [
43
43
  "multiclass_interface/version.py",
44
- "multiclass_interface/mpi_utils.py",
45
- "multiclass_interface/mpi_interface.py",
46
- "multiclass_interface/tests/my_test_cls.py",
47
44
  "multiclass_interface/tests/*",
48
45
  ]
49
46
 
@@ -1,99 +0,0 @@
1
- import os
2
- import time
3
-
4
- from numpy import testing as npt
5
- import pytest
6
-
7
- from multiclass_interface.mpi_interface import MPIClassInterface
8
- from multiclass_interface.multiprocess_interface import ProcessClass, MultiProcessClassInterface
9
- import numpy as np
10
- from multiclass_interface.tests.my_test_cls import MyTest
11
- from multiclass_interface import mpi_interface
12
-
13
-
14
- def mpitest_mpi_MyTest():
15
-
16
- N = 4
17
- try:
18
- with MPIClassInterface(MyTest, [(i,) for i in range(N)]) as m:
19
-
20
- time.sleep(.1)
21
- i = m.get_id()
22
- npt.assert_array_equal(i, np.arange(N))
23
-
24
- i = mpi_interface.main_run(lambda: m.get_id())
25
- npt.assert_array_equal(i, 0)
26
- npt.assert_array_equal(m[1:3].get_id(), np.arange(1, 3))
27
- t = time.time()
28
- m.work(1)
29
- t = time.time() - t
30
- assert t < 1.1
31
-
32
- with pytest.raises(Exception, match='Cannot close SubsetMPIClassInterface. Please close all instances at once'):
33
- m[:3].close()
34
- with pytest.raises(Exception, match='Cannot make subset of SubsetMPIClassInterface'):
35
- m[:3][1]
36
-
37
- print("done, test_mpi_MyTest")
38
- except ChildProcessError:
39
- pass
40
-
41
-
42
- def mpitest_mpi_ProcessClass():
43
-
44
- with ProcessClass(MyTest) as cls:
45
- myTest = cls(1)
46
- assert myTest.get_id() == 1
47
- print("done, test_mpi_ProcessClass", flush=True)
48
-
49
-
50
- def mpitest_non_collective_mpi():
51
- mpi_interface.COLLECTIVE_MPI = False
52
- rank = mpi_interface.rank
53
- import time
54
- N = 3
55
- with MPIClassInterface(MyTest, [(i + 10,) for i in range(N)]) as m:
56
-
57
- try:
58
- assert m.get_id()[0] == rank + 10, m.get_id()
59
- main_id = mpi_interface.main_run(lambda: m.get_id())
60
- assert main_id == [10], (rank, main_id)
61
- assert rank < N # rank > N will fail with ChildProcessError
62
- except ChildProcessError:
63
- pass
64
-
65
- mpi_interface.COLLECTIVE_MPI = True
66
-
67
-
68
- def mpitest_all():
69
- mpi_interface.activate_mpi()
70
-
71
- mpi_interface.exit_mpi_on_close = False
72
- rank = mpi_interface.rank
73
- from multiclass_interface.tests.test_multiprocessinterface import test_attribute, test_missing_attribute, test_execption, test_setattr, test_setattr_method
74
- mpi_interface.TERMINATE_ON_CLOSE = False
75
-
76
- def run(f, *args):
77
- try:
78
- if rank == 0:
79
- print(f"Rank {rank} start {f.__name__}", flush=True)
80
- f(*args)
81
- except ChildProcessError:
82
- pass
83
- finally:
84
- if rank == 0:
85
- print(f"Rank {rank} done {f.__name__}", flush=True)
86
- for f in [mpitest_mpi_MyTest,
87
- mpitest_mpi_ProcessClass]:
88
- run(f)
89
-
90
- try:
91
- with MPIClassInterface(MyTest, [(1,), (2,), (3,)]) as mpici:
92
- for f in [test_attribute, test_missing_attribute, test_execption, test_setattr, test_setattr_method]:
93
- run(f, mpici)
94
- except ChildProcessError:
95
- pass
96
-
97
- run(mpitest_non_collective_mpi)
98
-
99
- print(f"Rank {rank} Done test_all")