yirgacheffe 1.3.1__tar.gz → 1.3.2__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.
Potentially problematic release.
This version of yirgacheffe might be problematic. Click here for more details.
- {yirgacheffe-1.3.1/yirgacheffe.egg-info → yirgacheffe-1.3.2}/PKG-INFO +1 -1
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/pyproject.toml +1 -1
- yirgacheffe-1.3.2/tests/test_parallel_operators.py +310 -0
- yirgacheffe-1.3.2/yirgacheffe/constants.py +2 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/yirgacheffe/operators.py +15 -4
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2/yirgacheffe.egg-info}/PKG-INFO +1 -1
- yirgacheffe-1.3.1/tests/test_parallel_operators.py +0 -255
- yirgacheffe-1.3.1/yirgacheffe/constants.py +0 -1
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/LICENSE +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/MANIFEST.in +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/README.md +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/setup.cfg +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/tests/test_area.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/tests/test_auto_windowing.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/tests/test_base.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/tests/test_datatypes.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/tests/test_group.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/tests/test_h3layer.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/tests/test_intersection.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/tests/test_multiband.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/tests/test_operators.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/tests/test_optimisation.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/tests/test_pickle.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/tests/test_raster.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/tests/test_rescaling.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/tests/test_rounding.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/tests/test_save_with_window.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/tests/test_sum_with_window.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/tests/test_uniform_area_layer.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/tests/test_union.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/tests/test_vectors.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/tests/test_window.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/yirgacheffe/__init__.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/yirgacheffe/backends/__init__.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/yirgacheffe/backends/enumeration.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/yirgacheffe/backends/mlx.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/yirgacheffe/backends/numpy.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/yirgacheffe/h3layer.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/yirgacheffe/layers/__init__.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/yirgacheffe/layers/area.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/yirgacheffe/layers/base.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/yirgacheffe/layers/constant.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/yirgacheffe/layers/group.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/yirgacheffe/layers/h3layer.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/yirgacheffe/layers/rasters.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/yirgacheffe/layers/rescaled.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/yirgacheffe/layers/vectors.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/yirgacheffe/rounding.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/yirgacheffe/window.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/yirgacheffe.egg-info/SOURCES.txt +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/yirgacheffe.egg-info/dependency_links.txt +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/yirgacheffe.egg-info/entry_points.txt +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/yirgacheffe.egg-info/requires.txt +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.2}/yirgacheffe.egg-info/top_level.txt +0 -0
|
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "yirgacheffe"
|
|
9
|
-
version = "1.3.
|
|
9
|
+
version = "1.3.2"
|
|
10
10
|
description = "Abstraction of gdal datasets for doing basic math operations"
|
|
11
11
|
readme = "README.md"
|
|
12
12
|
authors = [{ name = "Michael Dales", email = "mwd24@cam.ac.uk" }]
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import tempfile
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
import pytest
|
|
6
|
+
import torch
|
|
7
|
+
|
|
8
|
+
import yirgacheffe
|
|
9
|
+
from helpers import gdal_dataset_with_data
|
|
10
|
+
from yirgacheffe.layers import RasterLayer, ConstantLayer
|
|
11
|
+
from yirgacheffe.operators import LayerOperation
|
|
12
|
+
|
|
13
|
+
# These tests are marked skip for MLX, because there seems to be a problem with
|
|
14
|
+
# calling mx.eval in the tests for parallel save on Linux (which is what we use
|
|
15
|
+
# for github actions for instance). They do pass on macOS - but that could also
|
|
16
|
+
# just be down to Python versions. To add further complication, if I just run
|
|
17
|
+
# this file along on linux with MLX it passes fine 🤦
|
|
18
|
+
#
|
|
19
|
+
# It seems that under the hood MLX is doing some threading of its own and my
|
|
20
|
+
# guess is that that's interacting with the Python threading here.
|
|
21
|
+
|
|
22
|
+
def test_add_byte_layers_with_one_thread_uses_regular_save(monkeypatch) -> None:
|
|
23
|
+
with monkeypatch.context() as m:
|
|
24
|
+
m.setattr(yirgacheffe.constants, "YSTEP", 4)
|
|
25
|
+
m.setattr(LayerOperation, "save", None)
|
|
26
|
+
with tempfile.TemporaryDirectory() as tempdir:
|
|
27
|
+
path1 = os.path.join(tempdir, "test1.tif")
|
|
28
|
+
data1 = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
|
|
29
|
+
dataset1 = gdal_dataset_with_data((0.0, 0.0), 0.02, data1, filename=path1)
|
|
30
|
+
dataset1.Close()
|
|
31
|
+
|
|
32
|
+
path2 = os.path.join(tempdir, "test2.tif")
|
|
33
|
+
data2 = np.array([[10, 20, 30, 40], [50, 60, 70, 80]])
|
|
34
|
+
dataset2 = gdal_dataset_with_data((0.0, 0.0), 0.02, data2, filename=path2)
|
|
35
|
+
dataset2.Close()
|
|
36
|
+
|
|
37
|
+
layer1 = RasterLayer.layer_from_file(path1)
|
|
38
|
+
layer2 = RasterLayer.layer_from_file(path2)
|
|
39
|
+
result = RasterLayer.empty_raster_layer_like(layer1)
|
|
40
|
+
|
|
41
|
+
comp = layer1 + layer2
|
|
42
|
+
with pytest.raises(TypeError):
|
|
43
|
+
comp.parallel_save(result)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@pytest.mark.skipif(yirgacheffe.backends.BACKEND != "NUMPY", reason="Only applies for numpy")
|
|
47
|
+
def test_add_byte_layers(monkeypatch) -> None:
|
|
48
|
+
with monkeypatch.context() as m:
|
|
49
|
+
m.setattr(yirgacheffe.constants, "YSTEP", 1)
|
|
50
|
+
m.setattr(LayerOperation, "save", None)
|
|
51
|
+
with tempfile.TemporaryDirectory() as tempdir:
|
|
52
|
+
path1 = os.path.join(tempdir, "test1.tif")
|
|
53
|
+
data1 = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
|
|
54
|
+
dataset1 = gdal_dataset_with_data((0.0, 0.0), 0.02, data1, filename=path1)
|
|
55
|
+
dataset1.Close()
|
|
56
|
+
|
|
57
|
+
path2 = os.path.join(tempdir, "test2.tif")
|
|
58
|
+
data2 = np.array([[10, 20, 30, 40], [50, 60, 70, 80]])
|
|
59
|
+
dataset2 = gdal_dataset_with_data((0.0, 0.0), 0.02, data2, filename=path2)
|
|
60
|
+
dataset2.Close()
|
|
61
|
+
|
|
62
|
+
layer1 = RasterLayer.layer_from_file(path1)
|
|
63
|
+
layer2 = RasterLayer.layer_from_file(path2)
|
|
64
|
+
result = RasterLayer.empty_raster_layer_like(layer1)
|
|
65
|
+
|
|
66
|
+
comp = layer1 + layer2
|
|
67
|
+
comp.parallel_save(result)
|
|
68
|
+
|
|
69
|
+
expected = data1 + data2
|
|
70
|
+
actual = result.read_array(0, 0, 4, 2)
|
|
71
|
+
|
|
72
|
+
assert (expected == actual).all()
|
|
73
|
+
|
|
74
|
+
@pytest.mark.skipif(yirgacheffe.backends.BACKEND != "NUMPY", reason="Only applies for numpy")
|
|
75
|
+
def test_add_byte_layers_and_sum(monkeypatch) -> None:
|
|
76
|
+
with monkeypatch.context() as m:
|
|
77
|
+
m.setattr(yirgacheffe.constants, "YSTEP", 1)
|
|
78
|
+
m.setattr(LayerOperation, "save", None)
|
|
79
|
+
with tempfile.TemporaryDirectory() as tempdir:
|
|
80
|
+
path1 = os.path.join(tempdir, "test1.tif")
|
|
81
|
+
data1 = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
|
|
82
|
+
dataset1 = gdal_dataset_with_data((0.0, 0.0), 0.02, data1, filename=path1)
|
|
83
|
+
dataset1.Close()
|
|
84
|
+
|
|
85
|
+
path2 = os.path.join(tempdir, "test2.tif")
|
|
86
|
+
data2 = np.array([[10, 20, 30, 40], [50, 60, 70, 80]])
|
|
87
|
+
dataset2 = gdal_dataset_with_data((0.0, 0.0), 0.02, data2, filename=path2)
|
|
88
|
+
dataset2.Close()
|
|
89
|
+
|
|
90
|
+
layer1 = RasterLayer.layer_from_file(path1)
|
|
91
|
+
layer2 = RasterLayer.layer_from_file(path2)
|
|
92
|
+
result = RasterLayer.empty_raster_layer_like(layer1)
|
|
93
|
+
|
|
94
|
+
comp = layer1 + layer2
|
|
95
|
+
sum_total = comp.parallel_save(result, and_sum=True)
|
|
96
|
+
|
|
97
|
+
expected = data1 + data2
|
|
98
|
+
actual = result.read_array(0, 0, 4, 2)
|
|
99
|
+
|
|
100
|
+
assert (expected == actual).all()
|
|
101
|
+
assert sum_total == expected.sum()
|
|
102
|
+
|
|
103
|
+
@pytest.mark.skipif(yirgacheffe.backends.BACKEND != "NUMPY", reason="Only applies for numpy")
|
|
104
|
+
def test_parallel_sum(monkeypatch) -> None:
|
|
105
|
+
with monkeypatch.context() as m:
|
|
106
|
+
m.setattr(yirgacheffe.constants, "YSTEP", 1)
|
|
107
|
+
m.setattr(LayerOperation, "save", None)
|
|
108
|
+
with tempfile.TemporaryDirectory() as tempdir:
|
|
109
|
+
path1 = os.path.join(tempdir, "test1.tif")
|
|
110
|
+
data1 = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
|
|
111
|
+
dataset1 = gdal_dataset_with_data((0.0, 0.0), 0.02, data1, filename=path1)
|
|
112
|
+
dataset1.Close()
|
|
113
|
+
|
|
114
|
+
path2 = os.path.join(tempdir, "test2.tif")
|
|
115
|
+
data2 = np.array([[10, 20, 30, 40], [50, 60, 70, 80]])
|
|
116
|
+
dataset2 = gdal_dataset_with_data((0.0, 0.0), 0.02, data2, filename=path2)
|
|
117
|
+
dataset2.Close()
|
|
118
|
+
|
|
119
|
+
layer1 = RasterLayer.layer_from_file(path1)
|
|
120
|
+
layer2 = RasterLayer.layer_from_file(path2)
|
|
121
|
+
|
|
122
|
+
comp = layer1 + layer2
|
|
123
|
+
sum_total = comp.parallel_sum()
|
|
124
|
+
|
|
125
|
+
expected = data1 + data2
|
|
126
|
+
assert sum_total == expected.sum()
|
|
127
|
+
|
|
128
|
+
@pytest.mark.skipif(yirgacheffe.backends.BACKEND != "NUMPY", reason="Only applies for numpy")
|
|
129
|
+
@pytest.mark.parametrize("skip,expected_steps", [
|
|
130
|
+
(1, [0.0, 0.25, 0.5, 0.75, 1.0]),
|
|
131
|
+
(2, [0.0, 0.5, 1.0]),
|
|
132
|
+
])
|
|
133
|
+
def test_parallel_with_different_skip(monkeypatch, skip, expected_steps) -> None:
|
|
134
|
+
with monkeypatch.context() as m:
|
|
135
|
+
m.setattr(yirgacheffe.constants, "YSTEP", 1)
|
|
136
|
+
m.setattr(LayerOperation, "save", None)
|
|
137
|
+
with tempfile.TemporaryDirectory() as tempdir:
|
|
138
|
+
path1 = os.path.join(tempdir, "test1.tif")
|
|
139
|
+
data1 = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [1, 2, 3, 4], [5, 6, 7, 8]])
|
|
140
|
+
dataset1 = gdal_dataset_with_data((0.0, 0.0), 0.02, data1, filename=path1)
|
|
141
|
+
dataset1.Close()
|
|
142
|
+
|
|
143
|
+
path2 = os.path.join(tempdir, "test2.tif")
|
|
144
|
+
data2 = np.array([[10, 20, 30, 40], [50, 60, 70, 80], [10, 20, 30, 40], [50, 60, 70, 80]])
|
|
145
|
+
dataset2 = gdal_dataset_with_data((0.0, 0.0), 0.02, data2, filename=path2)
|
|
146
|
+
dataset2.Close()
|
|
147
|
+
|
|
148
|
+
layer1 = RasterLayer.layer_from_file(path1)
|
|
149
|
+
layer2 = RasterLayer.layer_from_file(path2)
|
|
150
|
+
result = RasterLayer.empty_raster_layer_like(layer1)
|
|
151
|
+
|
|
152
|
+
callback_possitions = []
|
|
153
|
+
|
|
154
|
+
comp = layer1 + layer2
|
|
155
|
+
comp.ystep = skip
|
|
156
|
+
comp.parallel_save(result, callback=lambda x: callback_possitions.append(x))
|
|
157
|
+
|
|
158
|
+
expected = data1 + data2
|
|
159
|
+
actual = result.read_array(0, 0, 4, 4)
|
|
160
|
+
|
|
161
|
+
assert (expected == actual).all()
|
|
162
|
+
|
|
163
|
+
assert callback_possitions == expected_steps
|
|
164
|
+
|
|
165
|
+
@pytest.mark.skipif(yirgacheffe.backends.BACKEND != "NUMPY", reason="Only applies for numpy")
|
|
166
|
+
def test_parallel_equality(monkeypatch) -> None:
|
|
167
|
+
with monkeypatch.context() as m:
|
|
168
|
+
m.setattr(yirgacheffe.constants, "YSTEP", 1)
|
|
169
|
+
m.setattr(LayerOperation, "save", None)
|
|
170
|
+
with tempfile.TemporaryDirectory() as tempdir:
|
|
171
|
+
path1 = os.path.join(tempdir, "test1.tif")
|
|
172
|
+
data1 = np.array([[1, 2, 3, 4], [4, 3, 2, 1]])
|
|
173
|
+
dataset1 = gdal_dataset_with_data((0.0, 0.0), 0.02, data1, filename=path1)
|
|
174
|
+
dataset1.Close()
|
|
175
|
+
with RasterLayer.layer_from_file(path1) as layer1:
|
|
176
|
+
with RasterLayer.empty_raster_layer_like(layer1) as result:
|
|
177
|
+
comp = layer1 == 2
|
|
178
|
+
comp.parallel_save(result)
|
|
179
|
+
|
|
180
|
+
expected = np.array([[0, 1, 0, 0], [0, 0, 1, 0]])
|
|
181
|
+
actual = result.read_array(0, 0, 4, 2)
|
|
182
|
+
|
|
183
|
+
assert (expected == actual).all()
|
|
184
|
+
|
|
185
|
+
@pytest.mark.skipif(yirgacheffe.backends.BACKEND != "NUMPY", reason="Only applies for numpy")
|
|
186
|
+
def test_parallel_equality_to_file(monkeypatch) -> None:
|
|
187
|
+
with monkeypatch.context() as m:
|
|
188
|
+
m.setattr(yirgacheffe.constants, "YSTEP", 1)
|
|
189
|
+
m.setattr(LayerOperation, "save", None)
|
|
190
|
+
with tempfile.TemporaryDirectory() as tempdir:
|
|
191
|
+
path1 = os.path.join(tempdir, "test1.tif")
|
|
192
|
+
path2 = os.path.join(tempdir, "result.tif")
|
|
193
|
+
data1 = np.array([[1, 2, 3, 4], [4, 3, 2, 1]])
|
|
194
|
+
dataset1 = gdal_dataset_with_data((0.0, 0.0), 0.02, data1, filename=path1)
|
|
195
|
+
dataset1.Close()
|
|
196
|
+
with RasterLayer.layer_from_file(path1) as layer1:
|
|
197
|
+
comp = layer1 == 2
|
|
198
|
+
with RasterLayer.empty_raster_layer_like(layer1, filename=path2) as result:
|
|
199
|
+
comp.parallel_save(result)
|
|
200
|
+
with RasterLayer.layer_from_file(path2) as actual_result:
|
|
201
|
+
expected = np.array([[0, 1, 0, 0], [0, 0, 1, 0]])
|
|
202
|
+
actual = actual_result.read_array(0, 0, 4, 2)
|
|
203
|
+
assert (expected == actual).all()
|
|
204
|
+
|
|
205
|
+
@pytest.mark.skipif(yirgacheffe.backends.BACKEND != "NUMPY", reason="Only applies for numpy")
|
|
206
|
+
def test_parallel_unary_numpy_apply_with_function(monkeypatch) -> None:
|
|
207
|
+
with monkeypatch.context() as m:
|
|
208
|
+
m.setattr(yirgacheffe.constants, "YSTEP", 1)
|
|
209
|
+
m.setattr(LayerOperation, "save", None)
|
|
210
|
+
with tempfile.TemporaryDirectory() as tempdir:
|
|
211
|
+
path1 = os.path.join(tempdir, "test1.tif")
|
|
212
|
+
data1 = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
|
|
213
|
+
dataset1 = gdal_dataset_with_data((0.0, 0.0), 0.02, data1, filename=path1)
|
|
214
|
+
dataset1.Close()
|
|
215
|
+
layer1 = RasterLayer.layer_from_file(path1)
|
|
216
|
+
|
|
217
|
+
result = RasterLayer.empty_raster_layer_like(layer1)
|
|
218
|
+
|
|
219
|
+
def simple_add(chunk):
|
|
220
|
+
return chunk + 1.0
|
|
221
|
+
|
|
222
|
+
comp = layer1.numpy_apply(simple_add)
|
|
223
|
+
comp.ystep = 1
|
|
224
|
+
comp.parallel_save(result)
|
|
225
|
+
|
|
226
|
+
expected = data1 + 1.0
|
|
227
|
+
actual = result.read_array(0, 0, 4, 2)
|
|
228
|
+
|
|
229
|
+
assert (expected == actual).all()
|
|
230
|
+
|
|
231
|
+
@pytest.mark.skipif(yirgacheffe.backends.BACKEND != "NUMPY", reason="Only applies for numpy")
|
|
232
|
+
def test_parallel_unary_numpy_apply_with_lambda(monkeypatch) -> None:
|
|
233
|
+
with monkeypatch.context() as m:
|
|
234
|
+
m.setattr(yirgacheffe.constants, "YSTEP", 1)
|
|
235
|
+
m.setattr(LayerOperation, "save", None)
|
|
236
|
+
with tempfile.TemporaryDirectory() as tempdir:
|
|
237
|
+
path1 = os.path.join(tempdir, "test1.tif")
|
|
238
|
+
data1 = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
|
|
239
|
+
dataset1 = gdal_dataset_with_data((0.0, 0.0), 0.02, data1, filename=path1)
|
|
240
|
+
dataset1.Close()
|
|
241
|
+
layer1 = RasterLayer.layer_from_file(path1)
|
|
242
|
+
|
|
243
|
+
result = RasterLayer.empty_raster_layer_like(layer1)
|
|
244
|
+
|
|
245
|
+
comp = layer1.numpy_apply(lambda a: a + 1.0)
|
|
246
|
+
comp.ystep = 1
|
|
247
|
+
comp.parallel_save(result)
|
|
248
|
+
|
|
249
|
+
expected = data1 + 1.0
|
|
250
|
+
actual = result.read_array(0, 0, 4, 2)
|
|
251
|
+
|
|
252
|
+
assert (expected == actual).all()
|
|
253
|
+
|
|
254
|
+
@pytest.mark.skipif(yirgacheffe.backends.BACKEND != "NUMPY", reason="Only applies for numpy")
|
|
255
|
+
def test_parallel_where_simple(monkeypatch) -> None:
|
|
256
|
+
with monkeypatch.context() as m:
|
|
257
|
+
m.setattr(yirgacheffe.constants, "YSTEP", 1)
|
|
258
|
+
m.setattr(LayerOperation, "save", None)
|
|
259
|
+
with tempfile.TemporaryDirectory() as tempdir:
|
|
260
|
+
path1 = os.path.join(tempdir, "test1.tif")
|
|
261
|
+
data1 = np.array([[0, 1, 0, 2], [0, 0, 1, 1]])
|
|
262
|
+
dataset1 = gdal_dataset_with_data((0.0, 0.0), 0.02, data1, filename=path1)
|
|
263
|
+
dataset1.Close()
|
|
264
|
+
layer1 = RasterLayer.layer_from_file(path1)
|
|
265
|
+
|
|
266
|
+
result = RasterLayer.empty_raster_layer_like(layer1)
|
|
267
|
+
|
|
268
|
+
comp = LayerOperation.where(layer1 > 0, 1, 2)
|
|
269
|
+
comp.ystep = 1
|
|
270
|
+
comp.parallel_save(result)
|
|
271
|
+
|
|
272
|
+
expected = np.where(data1 > 0, 1, 2)
|
|
273
|
+
actual = result.read_array(0, 0, 4, 2)
|
|
274
|
+
assert (expected == actual).all()
|
|
275
|
+
|
|
276
|
+
@pytest.mark.skipif(yirgacheffe.backends.BACKEND != "NUMPY", reason="Only applies for numpy")
|
|
277
|
+
def test_parallel_conv2d() -> None:
|
|
278
|
+
with tempfile.TemporaryDirectory() as tempdir:
|
|
279
|
+
|
|
280
|
+
data1 = np.array([
|
|
281
|
+
[0, 0, 0, 0, 0],
|
|
282
|
+
[0, 1, 1, 1, 0],
|
|
283
|
+
[0, 1, 1, 1, 0],
|
|
284
|
+
[0, 1, 1, 1, 0],
|
|
285
|
+
[0, 0, 0, 0, 0],
|
|
286
|
+
]).astype(np.float32)
|
|
287
|
+
weights = np.array([
|
|
288
|
+
[0.0, 0.1, 0.0],
|
|
289
|
+
[0.1, 0.6, 0.1],
|
|
290
|
+
[0.0, 0.1, 0.0],
|
|
291
|
+
])
|
|
292
|
+
|
|
293
|
+
conv = torch.nn.Conv2d(1, 1, 3, padding=1, bias=False)
|
|
294
|
+
conv.weight = torch.nn.Parameter(torch.from_numpy(np.array([[weights.astype(np.float32)]])))
|
|
295
|
+
tensorres = conv(torch.from_numpy(np.array([[data1]])))
|
|
296
|
+
expected = tensorres.detach().numpy()[0][0]
|
|
297
|
+
|
|
298
|
+
path1 = os.path.join(tempdir, "test1.tif")
|
|
299
|
+
dataset1 = gdal_dataset_with_data((0.0, 0.0), 0.02, data1, filename=path1)
|
|
300
|
+
dataset1.Close()
|
|
301
|
+
|
|
302
|
+
with RasterLayer.layer_from_file(path1) as layer1:
|
|
303
|
+
|
|
304
|
+
calc = layer1.conv2d(weights)
|
|
305
|
+
with RasterLayer.empty_raster_layer_like(layer1) as res:
|
|
306
|
+
calc.save(res)
|
|
307
|
+
actual = res.read_array(0, 0, 5, 5)
|
|
308
|
+
|
|
309
|
+
# Torch and MLX give slightly different rounding
|
|
310
|
+
assert np.isclose(expected, actual).all()
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
import math
|
|
2
3
|
import multiprocessing
|
|
3
4
|
import sys
|
|
4
5
|
import time
|
|
@@ -578,6 +579,20 @@ class LayerOperation(LayerMathMixin):
|
|
|
578
579
|
def _parallel_save(self, destination_layer, and_sum=False, callback=None, parallelism=None, band=1):
|
|
579
580
|
assert (destination_layer is not None) or and_sum
|
|
580
581
|
computation_window = self.window
|
|
582
|
+
|
|
583
|
+
worker_count = parallelism or multiprocessing.cpu_count()
|
|
584
|
+
work_blocks = len(range(0, computation_window.ysize, self.ystep))
|
|
585
|
+
adjusted_blocks = math.ceil(work_blocks / constants.MINIMUM_CHUNKS_PER_THREAD)
|
|
586
|
+
worker_count = min(adjusted_blocks, worker_count)
|
|
587
|
+
|
|
588
|
+
if worker_count == 1:
|
|
589
|
+
if destination_layer:
|
|
590
|
+
return self.save(destination_layer, and_sum, callback, band)
|
|
591
|
+
elif and_sum:
|
|
592
|
+
return self.sum()
|
|
593
|
+
else:
|
|
594
|
+
assert False
|
|
595
|
+
|
|
581
596
|
if destination_layer is not None:
|
|
582
597
|
try:
|
|
583
598
|
band = destination_layer._dataset.GetRasterBand(band)
|
|
@@ -615,10 +630,6 @@ class LayerOperation(LayerMathMixin):
|
|
|
615
630
|
with multiprocessing.Manager() as manager:
|
|
616
631
|
with SharedMemoryManager() as smm:
|
|
617
632
|
|
|
618
|
-
worker_count = parallelism or multiprocessing.cpu_count()
|
|
619
|
-
work_blocks = len(range(0, computation_window.ysize, self.ystep))
|
|
620
|
-
worker_count = min(work_blocks, worker_count)
|
|
621
|
-
|
|
622
633
|
mem_sem_cast = []
|
|
623
634
|
for i in range(worker_count):
|
|
624
635
|
shared_buf = smm.SharedMemory(size=np_dtype.itemsize * self.ystep * computation_window.xsize)
|
|
@@ -1,255 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import tempfile
|
|
3
|
-
|
|
4
|
-
import numpy as np
|
|
5
|
-
import pytest
|
|
6
|
-
import torch
|
|
7
|
-
|
|
8
|
-
import yirgacheffe
|
|
9
|
-
from helpers import gdal_dataset_with_data
|
|
10
|
-
from yirgacheffe.layers import RasterLayer, ConstantLayer
|
|
11
|
-
from yirgacheffe.operators import LayerOperation
|
|
12
|
-
|
|
13
|
-
# These tests are marked skip for MLX, because there seems to be a problem with
|
|
14
|
-
# calling mx.eval in the tests for parallel save on Linux (which is what we use
|
|
15
|
-
# for github actions for instance). They do pass on macOS - but that could also
|
|
16
|
-
# just be down to Python versions. To add further complication, if I just run
|
|
17
|
-
# this file along on linux with MLX it passes fine 🤦
|
|
18
|
-
#
|
|
19
|
-
# It seems that under the hood MLX is doing some threading of its own and my
|
|
20
|
-
# guess is that that's interacting with the Python threading here.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
@pytest.mark.skipif(yirgacheffe.backends.BACKEND != "NUMPY", reason="Only applies for numpy")
|
|
24
|
-
def test_add_byte_layers() -> None:
|
|
25
|
-
with tempfile.TemporaryDirectory() as tempdir:
|
|
26
|
-
path1 = os.path.join(tempdir, "test1.tif")
|
|
27
|
-
data1 = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
|
|
28
|
-
dataset1 = gdal_dataset_with_data((0.0, 0.0), 0.02, data1, filename=path1)
|
|
29
|
-
dataset1.Close()
|
|
30
|
-
|
|
31
|
-
path2 = os.path.join(tempdir, "test2.tif")
|
|
32
|
-
data2 = np.array([[10, 20, 30, 40], [50, 60, 70, 80]])
|
|
33
|
-
dataset2 = gdal_dataset_with_data((0.0, 0.0), 0.02, data2, filename=path2)
|
|
34
|
-
dataset2.Close()
|
|
35
|
-
|
|
36
|
-
layer1 = RasterLayer.layer_from_file(path1)
|
|
37
|
-
layer2 = RasterLayer.layer_from_file(path2)
|
|
38
|
-
result = RasterLayer.empty_raster_layer_like(layer1)
|
|
39
|
-
|
|
40
|
-
comp = layer1 + layer2
|
|
41
|
-
comp.parallel_save(result)
|
|
42
|
-
|
|
43
|
-
expected = data1 + data2
|
|
44
|
-
actual = result.read_array(0, 0, 4, 2)
|
|
45
|
-
|
|
46
|
-
assert (expected == actual).all()
|
|
47
|
-
|
|
48
|
-
@pytest.mark.skipif(yirgacheffe.backends.BACKEND != "NUMPY", reason="Only applies for numpy")
|
|
49
|
-
def test_add_byte_layers_and_sum() -> None:
|
|
50
|
-
with tempfile.TemporaryDirectory() as tempdir:
|
|
51
|
-
path1 = os.path.join(tempdir, "test1.tif")
|
|
52
|
-
data1 = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
|
|
53
|
-
dataset1 = gdal_dataset_with_data((0.0, 0.0), 0.02, data1, filename=path1)
|
|
54
|
-
dataset1.Close()
|
|
55
|
-
|
|
56
|
-
path2 = os.path.join(tempdir, "test2.tif")
|
|
57
|
-
data2 = np.array([[10, 20, 30, 40], [50, 60, 70, 80]])
|
|
58
|
-
dataset2 = gdal_dataset_with_data((0.0, 0.0), 0.02, data2, filename=path2)
|
|
59
|
-
dataset2.Close()
|
|
60
|
-
|
|
61
|
-
layer1 = RasterLayer.layer_from_file(path1)
|
|
62
|
-
layer2 = RasterLayer.layer_from_file(path2)
|
|
63
|
-
result = RasterLayer.empty_raster_layer_like(layer1)
|
|
64
|
-
|
|
65
|
-
comp = layer1 + layer2
|
|
66
|
-
sum_total = comp.parallel_save(result, and_sum=True)
|
|
67
|
-
|
|
68
|
-
expected = data1 + data2
|
|
69
|
-
actual = result.read_array(0, 0, 4, 2)
|
|
70
|
-
|
|
71
|
-
assert (expected == actual).all()
|
|
72
|
-
assert sum_total == expected.sum()
|
|
73
|
-
|
|
74
|
-
@pytest.mark.skipif(yirgacheffe.backends.BACKEND != "NUMPY", reason="Only applies for numpy")
|
|
75
|
-
def test_parallel_sum() -> None:
|
|
76
|
-
with tempfile.TemporaryDirectory() as tempdir:
|
|
77
|
-
path1 = os.path.join(tempdir, "test1.tif")
|
|
78
|
-
data1 = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
|
|
79
|
-
dataset1 = gdal_dataset_with_data((0.0, 0.0), 0.02, data1, filename=path1)
|
|
80
|
-
dataset1.Close()
|
|
81
|
-
|
|
82
|
-
path2 = os.path.join(tempdir, "test2.tif")
|
|
83
|
-
data2 = np.array([[10, 20, 30, 40], [50, 60, 70, 80]])
|
|
84
|
-
dataset2 = gdal_dataset_with_data((0.0, 0.0), 0.02, data2, filename=path2)
|
|
85
|
-
dataset2.Close()
|
|
86
|
-
|
|
87
|
-
layer1 = RasterLayer.layer_from_file(path1)
|
|
88
|
-
layer2 = RasterLayer.layer_from_file(path2)
|
|
89
|
-
|
|
90
|
-
comp = layer1 + layer2
|
|
91
|
-
sum_total = comp.parallel_sum()
|
|
92
|
-
|
|
93
|
-
expected = data1 + data2
|
|
94
|
-
assert sum_total == expected.sum()
|
|
95
|
-
|
|
96
|
-
@pytest.mark.skipif(yirgacheffe.backends.BACKEND != "NUMPY", reason="Only applies for numpy")
|
|
97
|
-
@pytest.mark.parametrize("skip,expected_steps", [
|
|
98
|
-
(1, [0.0, 0.5, 1.0]),
|
|
99
|
-
(2, [0.0, 1.0]),
|
|
100
|
-
])
|
|
101
|
-
def test_parallel_with_different_skip(skip, expected_steps) -> None:
|
|
102
|
-
with tempfile.TemporaryDirectory() as tempdir:
|
|
103
|
-
path1 = os.path.join(tempdir, "test1.tif")
|
|
104
|
-
data1 = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
|
|
105
|
-
dataset1 = gdal_dataset_with_data((0.0, 0.0), 0.02, data1, filename=path1)
|
|
106
|
-
dataset1.Close()
|
|
107
|
-
|
|
108
|
-
path2 = os.path.join(tempdir, "test2.tif")
|
|
109
|
-
data2 = np.array([[10, 20, 30, 40], [50, 60, 70, 80]])
|
|
110
|
-
dataset2 = gdal_dataset_with_data((0.0, 0.0), 0.02, data2, filename=path2)
|
|
111
|
-
dataset2.Close()
|
|
112
|
-
|
|
113
|
-
layer1 = RasterLayer.layer_from_file(path1)
|
|
114
|
-
layer2 = RasterLayer.layer_from_file(path2)
|
|
115
|
-
result = RasterLayer.empty_raster_layer_like(layer1)
|
|
116
|
-
|
|
117
|
-
callback_possitions = []
|
|
118
|
-
|
|
119
|
-
comp = layer1 + layer2
|
|
120
|
-
comp.ystep = skip
|
|
121
|
-
comp.parallel_save(result, callback=lambda x: callback_possitions.append(x))
|
|
122
|
-
|
|
123
|
-
expected = data1 + data2
|
|
124
|
-
actual = result.read_array(0, 0, 4, 2)
|
|
125
|
-
|
|
126
|
-
assert (expected == actual).all()
|
|
127
|
-
|
|
128
|
-
assert callback_possitions == expected_steps
|
|
129
|
-
|
|
130
|
-
@pytest.mark.skipif(yirgacheffe.backends.BACKEND != "NUMPY", reason="Only applies for numpy")
|
|
131
|
-
def test_parallel_equality() -> None:
|
|
132
|
-
with tempfile.TemporaryDirectory() as tempdir:
|
|
133
|
-
path1 = os.path.join(tempdir, "test1.tif")
|
|
134
|
-
data1 = np.array([[1, 2, 3, 4], [4, 3, 2, 1]])
|
|
135
|
-
dataset1 = gdal_dataset_with_data((0.0, 0.0), 0.02, data1, filename=path1)
|
|
136
|
-
dataset1.Close()
|
|
137
|
-
with RasterLayer.layer_from_file(path1) as layer1:
|
|
138
|
-
with RasterLayer.empty_raster_layer_like(layer1) as result:
|
|
139
|
-
comp = layer1 == 2
|
|
140
|
-
comp.parallel_save(result)
|
|
141
|
-
|
|
142
|
-
expected = np.array([[0, 1, 0, 0], [0, 0, 1, 0]])
|
|
143
|
-
actual = result.read_array(0, 0, 4, 2)
|
|
144
|
-
|
|
145
|
-
assert (expected == actual).all()
|
|
146
|
-
|
|
147
|
-
@pytest.mark.skipif(yirgacheffe.backends.BACKEND != "NUMPY", reason="Only applies for numpy")
|
|
148
|
-
def test_parallel_equality_to_file() -> None:
|
|
149
|
-
with tempfile.TemporaryDirectory() as tempdir:
|
|
150
|
-
path1 = os.path.join(tempdir, "test1.tif")
|
|
151
|
-
path2 = os.path.join(tempdir, "result.tif")
|
|
152
|
-
data1 = np.array([[1, 2, 3, 4], [4, 3, 2, 1]])
|
|
153
|
-
dataset1 = gdal_dataset_with_data((0.0, 0.0), 0.02, data1, filename=path1)
|
|
154
|
-
dataset1.Close()
|
|
155
|
-
with RasterLayer.layer_from_file(path1) as layer1:
|
|
156
|
-
comp = layer1 == 2
|
|
157
|
-
with RasterLayer.empty_raster_layer_like(layer1, filename=path2) as result:
|
|
158
|
-
comp.parallel_save(result)
|
|
159
|
-
with RasterLayer.layer_from_file(path2) as actual_result:
|
|
160
|
-
expected = np.array([[0, 1, 0, 0], [0, 0, 1, 0]])
|
|
161
|
-
actual = actual_result.read_array(0, 0, 4, 2)
|
|
162
|
-
assert (expected == actual).all()
|
|
163
|
-
|
|
164
|
-
@pytest.mark.skipif(yirgacheffe.backends.BACKEND != "NUMPY", reason="Only applies for numpy")
|
|
165
|
-
def test_parallel_unary_numpy_apply_with_function() -> None:
|
|
166
|
-
with tempfile.TemporaryDirectory() as tempdir:
|
|
167
|
-
path1 = os.path.join(tempdir, "test1.tif")
|
|
168
|
-
data1 = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
|
|
169
|
-
dataset1 = gdal_dataset_with_data((0.0, 0.0), 0.02, data1, filename=path1)
|
|
170
|
-
dataset1.Close()
|
|
171
|
-
layer1 = RasterLayer.layer_from_file(path1)
|
|
172
|
-
|
|
173
|
-
result = RasterLayer.empty_raster_layer_like(layer1)
|
|
174
|
-
|
|
175
|
-
def simple_add(chunk):
|
|
176
|
-
return chunk + 1.0
|
|
177
|
-
|
|
178
|
-
comp = layer1.numpy_apply(simple_add)
|
|
179
|
-
comp.ystep = 1
|
|
180
|
-
comp.parallel_save(result)
|
|
181
|
-
|
|
182
|
-
expected = data1 + 1.0
|
|
183
|
-
actual = result.read_array(0, 0, 4, 2)
|
|
184
|
-
|
|
185
|
-
assert (expected == actual).all()
|
|
186
|
-
|
|
187
|
-
@pytest.mark.skipif(yirgacheffe.backends.BACKEND != "NUMPY", reason="Only applies for numpy")
|
|
188
|
-
def test_parallel_unary_numpy_apply_with_lambda() -> None:
|
|
189
|
-
with tempfile.TemporaryDirectory() as tempdir:
|
|
190
|
-
path1 = os.path.join(tempdir, "test1.tif")
|
|
191
|
-
data1 = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
|
|
192
|
-
dataset1 = gdal_dataset_with_data((0.0, 0.0), 0.02, data1, filename=path1)
|
|
193
|
-
dataset1.Close()
|
|
194
|
-
layer1 = RasterLayer.layer_from_file(path1)
|
|
195
|
-
|
|
196
|
-
result = RasterLayer.empty_raster_layer_like(layer1)
|
|
197
|
-
|
|
198
|
-
comp = layer1.numpy_apply(lambda a: a + 1.0)
|
|
199
|
-
comp.ystep = 1
|
|
200
|
-
comp.parallel_save(result)
|
|
201
|
-
|
|
202
|
-
expected = data1 + 1.0
|
|
203
|
-
actual = result.read_array(0, 0, 4, 2)
|
|
204
|
-
|
|
205
|
-
assert (expected == actual).all()
|
|
206
|
-
|
|
207
|
-
@pytest.mark.skipif(yirgacheffe.backends.BACKEND != "NUMPY", reason="Only applies for numpy")
|
|
208
|
-
def test_parallel_where_simple() -> None:
|
|
209
|
-
with tempfile.TemporaryDirectory() as tempdir:
|
|
210
|
-
path1 = os.path.join(tempdir, "test1.tif")
|
|
211
|
-
data1 = np.array([[0, 1, 0, 2], [0, 0, 1, 1]])
|
|
212
|
-
dataset1 = gdal_dataset_with_data((0.0, 0.0), 0.02, data1, filename=path1)
|
|
213
|
-
dataset1.Close()
|
|
214
|
-
layer1 = RasterLayer.layer_from_file(path1)
|
|
215
|
-
|
|
216
|
-
result = RasterLayer.empty_raster_layer_like(layer1)
|
|
217
|
-
|
|
218
|
-
comp = LayerOperation.where(layer1 > 0, 1, 2)
|
|
219
|
-
comp.ystep = 1
|
|
220
|
-
comp.parallel_save(result)
|
|
221
|
-
|
|
222
|
-
expected = np.where(data1 > 0, 1, 2)
|
|
223
|
-
actual = result.read_array(0, 0, 4, 2)
|
|
224
|
-
assert (expected == actual).all()
|
|
225
|
-
|
|
226
|
-
@pytest.mark.skipif(yirgacheffe.backends.BACKEND != "NUMPY", reason="Only applies for numpy")
|
|
227
|
-
def test_parallel_conv2d() -> None:
|
|
228
|
-
data1 = np.array([
|
|
229
|
-
[0, 0, 0, 0, 0],
|
|
230
|
-
[0, 1, 1, 1, 0],
|
|
231
|
-
[0, 1, 1, 1, 0],
|
|
232
|
-
[0, 1, 1, 1, 0],
|
|
233
|
-
[0, 0, 0, 0, 0],
|
|
234
|
-
]).astype(np.float32)
|
|
235
|
-
weights = np.array([
|
|
236
|
-
[0.0, 0.1, 0.0],
|
|
237
|
-
[0.1, 0.6, 0.1],
|
|
238
|
-
[0.0, 0.1, 0.0],
|
|
239
|
-
])
|
|
240
|
-
|
|
241
|
-
conv = torch.nn.Conv2d(1, 1, 3, padding=1, bias=False)
|
|
242
|
-
conv.weight = torch.nn.Parameter(torch.from_numpy(np.array([[weights.astype(np.float32)]])))
|
|
243
|
-
tensorres = conv(torch.from_numpy(np.array([[data1]])))
|
|
244
|
-
expected = tensorres.detach().numpy()[0][0]
|
|
245
|
-
|
|
246
|
-
with RasterLayer(gdal_dataset_with_data((0.0, 0.0), 0.02, data1)) as layer1:
|
|
247
|
-
|
|
248
|
-
calc = layer1.conv2d(weights)
|
|
249
|
-
calc.ystep = 1
|
|
250
|
-
with RasterLayer.empty_raster_layer_like(layer1) as res:
|
|
251
|
-
calc.save(res)
|
|
252
|
-
actual = res.read_array(0, 0, 5, 5)
|
|
253
|
-
|
|
254
|
-
# Torch and MLX give slightly different rounding
|
|
255
|
-
assert np.isclose(expected, actual).all()
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
YSTEP = 512
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|