yirgacheffe 1.3.1__tar.gz → 1.3.3__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.3}/PKG-INFO +4 -1
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/README.md +3 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/pyproject.toml +1 -1
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/tests/test_operators.py +84 -0
- yirgacheffe-1.3.3/tests/test_parallel_operators.py +310 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/yirgacheffe/backends/enumeration.py +3 -1
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/yirgacheffe/backends/mlx.py +6 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/yirgacheffe/backends/numpy.py +6 -0
- yirgacheffe-1.3.3/yirgacheffe/constants.py +2 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/yirgacheffe/operators.py +39 -4
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3/yirgacheffe.egg-info}/PKG-INFO +4 -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.3}/LICENSE +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/MANIFEST.in +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/setup.cfg +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/tests/test_area.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/tests/test_auto_windowing.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/tests/test_base.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/tests/test_datatypes.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/tests/test_group.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/tests/test_h3layer.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/tests/test_intersection.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/tests/test_multiband.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/tests/test_optimisation.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/tests/test_pickle.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/tests/test_raster.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/tests/test_rescaling.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/tests/test_rounding.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/tests/test_save_with_window.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/tests/test_sum_with_window.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/tests/test_uniform_area_layer.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/tests/test_union.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/tests/test_vectors.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/tests/test_window.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/yirgacheffe/__init__.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/yirgacheffe/backends/__init__.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/yirgacheffe/h3layer.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/yirgacheffe/layers/__init__.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/yirgacheffe/layers/area.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/yirgacheffe/layers/base.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/yirgacheffe/layers/constant.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/yirgacheffe/layers/group.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/yirgacheffe/layers/h3layer.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/yirgacheffe/layers/rasters.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/yirgacheffe/layers/rescaled.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/yirgacheffe/layers/vectors.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/yirgacheffe/rounding.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/yirgacheffe/window.py +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/yirgacheffe.egg-info/SOURCES.txt +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/yirgacheffe.egg-info/dependency_links.txt +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/yirgacheffe.egg-info/entry_points.txt +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/yirgacheffe.egg-info/requires.txt +0 -0
- {yirgacheffe-1.3.1 → yirgacheffe-1.3.3}/yirgacheffe.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: yirgacheffe
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.3
|
|
4
4
|
Summary: Abstraction of gdal datasets for doing basic math operations
|
|
5
5
|
Author-email: Michael Dales <mwd24@cam.ac.uk>
|
|
6
6
|
License-Expression: ISC
|
|
@@ -304,9 +304,11 @@ with RasterLayer.layer_from_file('test1.tif') as layer1:
|
|
|
304
304
|
The following math operators common to numpy and other libraries are currently supported:
|
|
305
305
|
|
|
306
306
|
* abs
|
|
307
|
+
* ceil
|
|
307
308
|
* clip
|
|
308
309
|
* exp
|
|
309
310
|
* exp2
|
|
311
|
+
* floor
|
|
310
312
|
* isin
|
|
311
313
|
* log
|
|
312
314
|
* log2
|
|
@@ -314,6 +316,7 @@ The following math operators common to numpy and other libraries are currently s
|
|
|
314
316
|
* maximum
|
|
315
317
|
* minimum
|
|
316
318
|
* nan_to_num
|
|
319
|
+
* round
|
|
317
320
|
|
|
318
321
|
Typically these can be invoked either on a layer as a method:
|
|
319
322
|
|
|
@@ -280,9 +280,11 @@ with RasterLayer.layer_from_file('test1.tif') as layer1:
|
|
|
280
280
|
The following math operators common to numpy and other libraries are currently supported:
|
|
281
281
|
|
|
282
282
|
* abs
|
|
283
|
+
* ceil
|
|
283
284
|
* clip
|
|
284
285
|
* exp
|
|
285
286
|
* exp2
|
|
287
|
+
* floor
|
|
286
288
|
* isin
|
|
287
289
|
* log
|
|
288
290
|
* log2
|
|
@@ -290,6 +292,7 @@ The following math operators common to numpy and other libraries are currently s
|
|
|
290
292
|
* maximum
|
|
291
293
|
* minimum
|
|
292
294
|
* nan_to_num
|
|
295
|
+
* round
|
|
293
296
|
|
|
294
297
|
Typically these can be invoked either on a layer as a method:
|
|
295
298
|
|
|
@@ -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.3"
|
|
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" }]
|
|
@@ -1332,3 +1332,87 @@ def test_simple_conv2d_over_calculated_result(skip) -> None:
|
|
|
1332
1332
|
|
|
1333
1333
|
# Torch and MLX give slightly different rounding
|
|
1334
1334
|
assert np.isclose(expected, actual).all()
|
|
1335
|
+
|
|
1336
|
+
def test_floor_method() -> None:
|
|
1337
|
+
data1 = np.array([[1.0, 1.2, 1.5, 1.7], [-1.0, -1.2, -1.5, -1.7]])
|
|
1338
|
+
layer1 = RasterLayer(gdal_dataset_with_data((0.0, 0.0), 0.02, data1))
|
|
1339
|
+
result = RasterLayer.empty_raster_layer_like(layer1)
|
|
1340
|
+
|
|
1341
|
+
comp = layer1.floor()
|
|
1342
|
+
comp.save(result)
|
|
1343
|
+
|
|
1344
|
+
expected = backend.floor_op(backend.promote(data1))
|
|
1345
|
+
backend.eval_op(expected)
|
|
1346
|
+
|
|
1347
|
+
actual = result.read_array(0, 0, 4, 2)
|
|
1348
|
+
assert (expected == actual).all()
|
|
1349
|
+
|
|
1350
|
+
def test_floor_module() -> None:
|
|
1351
|
+
data1 = np.array([[1.0, 1.2, 1.5, 1.7], [-1.0, -1.2, -1.5, -1.7]])
|
|
1352
|
+
layer1 = RasterLayer(gdal_dataset_with_data((0.0, 0.0), 0.02, data1))
|
|
1353
|
+
result = RasterLayer.empty_raster_layer_like(layer1)
|
|
1354
|
+
|
|
1355
|
+
comp = LayerOperation.floor(layer1)
|
|
1356
|
+
comp.save(result)
|
|
1357
|
+
|
|
1358
|
+
expected = backend.floor_op(backend.promote(data1))
|
|
1359
|
+
backend.eval_op(expected)
|
|
1360
|
+
|
|
1361
|
+
actual = result.read_array(0, 0, 4, 2)
|
|
1362
|
+
assert (expected == actual).all()
|
|
1363
|
+
|
|
1364
|
+
def test_round_method() -> None:
|
|
1365
|
+
data1 = np.array([[1.0, 1.2, 1.5, 1.7], [-1.0, -1.2, -1.5, -1.7]])
|
|
1366
|
+
layer1 = RasterLayer(gdal_dataset_with_data((0.0, 0.0), 0.02, data1))
|
|
1367
|
+
result = RasterLayer.empty_raster_layer_like(layer1)
|
|
1368
|
+
|
|
1369
|
+
comp = layer1.round()
|
|
1370
|
+
comp.save(result)
|
|
1371
|
+
|
|
1372
|
+
expected = backend.round_op(backend.promote(data1))
|
|
1373
|
+
backend.eval_op(expected)
|
|
1374
|
+
|
|
1375
|
+
actual = result.read_array(0, 0, 4, 2)
|
|
1376
|
+
assert (expected == actual).all()
|
|
1377
|
+
|
|
1378
|
+
def test_round_module() -> None:
|
|
1379
|
+
data1 = np.array([[1.0, 1.2, 1.5, 1.7], [-1.0, -1.2, -1.5, -1.7]])
|
|
1380
|
+
layer1 = RasterLayer(gdal_dataset_with_data((0.0, 0.0), 0.02, data1))
|
|
1381
|
+
result = RasterLayer.empty_raster_layer_like(layer1)
|
|
1382
|
+
|
|
1383
|
+
comp = LayerOperation.round(layer1)
|
|
1384
|
+
comp.save(result)
|
|
1385
|
+
|
|
1386
|
+
expected = backend.round_op(backend.promote(data1))
|
|
1387
|
+
backend.eval_op(expected)
|
|
1388
|
+
|
|
1389
|
+
actual = result.read_array(0, 0, 4, 2)
|
|
1390
|
+
assert (expected == actual).all()
|
|
1391
|
+
|
|
1392
|
+
def test_ceil_method() -> None:
|
|
1393
|
+
data1 = np.array([[1.0, 1.2, 1.5, 1.7], [-1.0, -1.2, -1.5, -1.7]])
|
|
1394
|
+
layer1 = RasterLayer(gdal_dataset_with_data((0.0, 0.0), 0.02, data1))
|
|
1395
|
+
result = RasterLayer.empty_raster_layer_like(layer1)
|
|
1396
|
+
|
|
1397
|
+
comp = layer1.ceil()
|
|
1398
|
+
comp.save(result)
|
|
1399
|
+
|
|
1400
|
+
expected = backend.ceil_op(backend.promote(data1))
|
|
1401
|
+
backend.eval_op(expected)
|
|
1402
|
+
|
|
1403
|
+
actual = result.read_array(0, 0, 4, 2)
|
|
1404
|
+
assert (expected == actual).all()
|
|
1405
|
+
|
|
1406
|
+
def test_ceil_module() -> None:
|
|
1407
|
+
data1 = np.array([[1.0, 1.2, 1.5, 1.7], [-1.0, -1.2, -1.5, -1.7]])
|
|
1408
|
+
layer1 = RasterLayer(gdal_dataset_with_data((0.0, 0.0), 0.02, data1))
|
|
1409
|
+
result = RasterLayer.empty_raster_layer_like(layer1)
|
|
1410
|
+
|
|
1411
|
+
comp = LayerOperation.ceil(layer1)
|
|
1412
|
+
comp.save(result)
|
|
1413
|
+
|
|
1414
|
+
expected = backend.ceil_op(backend.promote(data1))
|
|
1415
|
+
backend.eval_op(expected)
|
|
1416
|
+
|
|
1417
|
+
actual = result.read_array(0, 0, 4, 2)
|
|
1418
|
+
assert (expected == actual).all()
|
|
@@ -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()
|
|
@@ -45,6 +45,9 @@ allclose = mx.allclose
|
|
|
45
45
|
remainder_op = mx.remainder
|
|
46
46
|
floordiv_op = mx.array.__floordiv__
|
|
47
47
|
abs_op = mx.abs
|
|
48
|
+
floor_op = mx.floor
|
|
49
|
+
round_op = mx.round
|
|
50
|
+
ceil_op = mx.ceil
|
|
48
51
|
|
|
49
52
|
def sum_op(a):
|
|
50
53
|
# There are weird issues around how MLX overflows int8, so just promote the data ahead of summing
|
|
@@ -211,4 +214,7 @@ operator_map = {
|
|
|
211
214
|
op.CONV2D: conv2d_op,
|
|
212
215
|
op.ABS: mx.abs,
|
|
213
216
|
op.ASTYPE: astype_op,
|
|
217
|
+
op.FLOOR: mx.floor,
|
|
218
|
+
op.ROUND: mx.round,
|
|
219
|
+
op.CEIL: mx.ceil,
|
|
214
220
|
}
|
|
@@ -48,6 +48,9 @@ allclose = np.allclose
|
|
|
48
48
|
remainder_op = np.ndarray.__mod__
|
|
49
49
|
floordiv_op = np.ndarray.__floordiv__
|
|
50
50
|
abs_op = np.abs
|
|
51
|
+
floor_op = np.floor
|
|
52
|
+
round_op = np.round
|
|
53
|
+
ceil_op = np.ceil
|
|
51
54
|
|
|
52
55
|
def conv2d_op(data, weights):
|
|
53
56
|
# torch wants to process dimensions of channels of width of height
|
|
@@ -149,4 +152,7 @@ operator_map = {
|
|
|
149
152
|
op.CONV2D: conv2d_op,
|
|
150
153
|
op.ABS: np.abs,
|
|
151
154
|
op.ASTYPE: astype_op,
|
|
155
|
+
op.FLOOR: np.floor,
|
|
156
|
+
op.ROUND: np.round,
|
|
157
|
+
op.CEIL: np.ceil,
|
|
152
158
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
import math
|
|
2
3
|
import multiprocessing
|
|
3
4
|
import sys
|
|
4
5
|
import time
|
|
@@ -120,6 +121,27 @@ class LayerMathMixin:
|
|
|
120
121
|
window_op=WindowOperation.NONE,
|
|
121
122
|
)
|
|
122
123
|
|
|
124
|
+
def floor(self):
|
|
125
|
+
return LayerOperation(
|
|
126
|
+
self,
|
|
127
|
+
op.FLOOR,
|
|
128
|
+
window_op=WindowOperation.NONE,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
def round(self):
|
|
132
|
+
return LayerOperation(
|
|
133
|
+
self,
|
|
134
|
+
op.ROUND,
|
|
135
|
+
window_op=WindowOperation.NONE,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
def ceil(self):
|
|
139
|
+
return LayerOperation(
|
|
140
|
+
self,
|
|
141
|
+
op.CEIL,
|
|
142
|
+
window_op=WindowOperation.NONE,
|
|
143
|
+
)
|
|
144
|
+
|
|
123
145
|
def log(self):
|
|
124
146
|
return LayerOperation(
|
|
125
147
|
self,
|
|
@@ -578,6 +600,20 @@ class LayerOperation(LayerMathMixin):
|
|
|
578
600
|
def _parallel_save(self, destination_layer, and_sum=False, callback=None, parallelism=None, band=1):
|
|
579
601
|
assert (destination_layer is not None) or and_sum
|
|
580
602
|
computation_window = self.window
|
|
603
|
+
|
|
604
|
+
worker_count = parallelism or multiprocessing.cpu_count()
|
|
605
|
+
work_blocks = len(range(0, computation_window.ysize, self.ystep))
|
|
606
|
+
adjusted_blocks = math.ceil(work_blocks / constants.MINIMUM_CHUNKS_PER_THREAD)
|
|
607
|
+
worker_count = min(adjusted_blocks, worker_count)
|
|
608
|
+
|
|
609
|
+
if worker_count == 1:
|
|
610
|
+
if destination_layer:
|
|
611
|
+
return self.save(destination_layer, and_sum, callback, band)
|
|
612
|
+
elif and_sum:
|
|
613
|
+
return self.sum()
|
|
614
|
+
else:
|
|
615
|
+
assert False
|
|
616
|
+
|
|
581
617
|
if destination_layer is not None:
|
|
582
618
|
try:
|
|
583
619
|
band = destination_layer._dataset.GetRasterBand(band)
|
|
@@ -615,10 +651,6 @@ class LayerOperation(LayerMathMixin):
|
|
|
615
651
|
with multiprocessing.Manager() as manager:
|
|
616
652
|
with SharedMemoryManager() as smm:
|
|
617
653
|
|
|
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
654
|
mem_sem_cast = []
|
|
623
655
|
for i in range(worker_count):
|
|
624
656
|
shared_buf = smm.SharedMemory(size=np_dtype.itemsize * self.ystep * computation_window.xsize)
|
|
@@ -754,3 +786,6 @@ exp2 = LayerOperation.exp2
|
|
|
754
786
|
nan_to_num = LayerOperation.nan_to_num
|
|
755
787
|
isin = LayerOperation.isin
|
|
756
788
|
abs = LayerOperation.abs # pylint: disable=W0622
|
|
789
|
+
floor = LayerOperation.floor
|
|
790
|
+
round = LayerOperation.round # pylint: disable=W0622
|
|
791
|
+
ceil = LayerOperation.ceil
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: yirgacheffe
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.3
|
|
4
4
|
Summary: Abstraction of gdal datasets for doing basic math operations
|
|
5
5
|
Author-email: Michael Dales <mwd24@cam.ac.uk>
|
|
6
6
|
License-Expression: ISC
|
|
@@ -304,9 +304,11 @@ with RasterLayer.layer_from_file('test1.tif') as layer1:
|
|
|
304
304
|
The following math operators common to numpy and other libraries are currently supported:
|
|
305
305
|
|
|
306
306
|
* abs
|
|
307
|
+
* ceil
|
|
307
308
|
* clip
|
|
308
309
|
* exp
|
|
309
310
|
* exp2
|
|
311
|
+
* floor
|
|
310
312
|
* isin
|
|
311
313
|
* log
|
|
312
314
|
* log2
|
|
@@ -314,6 +316,7 @@ The following math operators common to numpy and other libraries are currently s
|
|
|
314
316
|
* maximum
|
|
315
317
|
* minimum
|
|
316
318
|
* nan_to_num
|
|
319
|
+
* round
|
|
317
320
|
|
|
318
321
|
Typically these can be invoked either on a layer as a method:
|
|
319
322
|
|
|
@@ -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
|