matrix-utils 0.6.3__tar.gz → 0.7.0__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.
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/CHANGELOG.md +4 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/PKG-INFO +1 -1
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/matrix_utils/__init__.py +1 -1
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/matrix_utils/array_mapper.py +14 -2
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/matrix_utils/mapped_matrix.py +29 -13
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/matrix_utils/resource_group.py +23 -3
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/matrix_utils.egg-info/PKG-INFO +1 -1
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/tests/array_mapper.py +25 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/tests/mapped_matrix.py +26 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/.gitignore +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/.pre-commit-config.yaml +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/CODE_OF_CONDUCT.md +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/LICENSE +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/MANIFEST.in +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/README.md +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/binder/Brightway 2.5 demonstration.ipynb +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/binder/environment.yml +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/dev/Brightway 2.5 - Linking with IAMs.ipynb +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/dev/Brightway 2.5 demonstration.ipynb +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/dev/Graph traversal with modification.ipynb +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/dev/Indexing speed tests.ipynb +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/dev/Interfaces which produce results for multiple matrices.ipynb +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/dev/Mapping speed tests.ipynb +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/dev/Multifunctionality with Brightway 2.5.ipynb +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/dev/Real-time modification of hybrid databases.ipynb +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/dev/Supply chain modification.ipynb +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/dev/Untitled.ipynb +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/dev/profiling/bw2.prof +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/dev/profiling/bw2.svg +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/dev/profiling/bw25.prof +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/dev/profiling/bw25.svg +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/dev/profiling/bw25_profile.txt +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/dev/profiling/bw25_profiling.py +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/dev/profiling/bw2_profile.txt +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/dev/profiling/bw2_profiling.py +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/dev/profiling/profiling-versus-bw2.py +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/dev/speed_test_unique_indices.py +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/dev/speed_tests.py +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/docs/Makefile +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/docs/conf.py +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/docs/index.rst +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/docs/loading.rst +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/docs/make.bat +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/docs/saving.rst +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/docs/utilities.rst +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/matrix_utils/aggregation.py +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/matrix_utils/errors.py +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/matrix_utils/indexers.py +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/matrix_utils/mapped_matrix_dict.py +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/matrix_utils/utils.py +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/matrix_utils.egg-info/SOURCES.txt +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/matrix_utils.egg-info/dependency_links.txt +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/matrix_utils.egg-info/requires.txt +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/matrix_utils.egg-info/top_level.txt +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/pyproject.toml +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/setup.cfg +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/tests/fixtures/__init__.py +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/tests/fixtures/a-first.zip +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/tests/fixtures/b-second.zip +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/tests/fixtures/basic.py +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/tests/fixtures/create_static_fixtures.py +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/tests/fixtures/sa-1.zip +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/tests/fixtures/sa-2.zip +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/tests/indexers.py +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/tests/integration.py +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/tests/interfaces.py +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/tests/masking.py +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/tests/monte_carlo.py +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/tests/ordering.py +0 -0
- {matrix_utils-0.6.3 → matrix_utils-0.7.0}/tests/utils.py +0 -0
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
### [0.7.0] - 2026-02-27
|
|
4
|
+
|
|
5
|
+
* Merge [#33](https://github.com/brightway-lca/matrix_utils/pull/33): Speed up large-index mapping by avoiding repeated unique and using fast integer dedupicator. Thanks @romainsacchi!
|
|
6
|
+
|
|
3
7
|
### [0.6.3] - 2026-02-23
|
|
4
8
|
|
|
5
9
|
* Fix [#32](https://github.com/brightway-lca/matrix_utils/issues/32): Replace deprecated `.A1` property on scipy sparse matrices
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import numpy as np
|
|
2
|
+
import pandas as pd
|
|
2
3
|
from scipy import sparse
|
|
3
4
|
|
|
4
5
|
from matrix_utils.errors import EmptyArray
|
|
@@ -25,10 +26,21 @@ class ArrayMapper:
|
|
|
25
26
|
""" # NOQA: E501
|
|
26
27
|
|
|
27
28
|
def __init__(self, *, array: np.ndarray, sparse_cutoff: int = 50000, empty_ok: bool = False):
|
|
29
|
+
|
|
30
|
+
|
|
28
31
|
self._check_input_array(array)
|
|
29
32
|
|
|
30
|
-
#
|
|
31
|
-
|
|
33
|
+
# `np.unique` can be very slow on large, unsorted integer arrays.
|
|
34
|
+
# Use hash-based `pandas.unique` first, then sort only the unique values.
|
|
35
|
+
# Fall back to NumPy if anything unexpected occurs.
|
|
36
|
+
if array.dtype.kind in {"i", "u"} and array.shape[0] > 100_000:
|
|
37
|
+
try:
|
|
38
|
+
self.array = np.sort(pd.unique(array))
|
|
39
|
+
except (TypeError, ValueError):
|
|
40
|
+
self.array = np.unique(array)
|
|
41
|
+
else:
|
|
42
|
+
self.array = np.unique(array)
|
|
43
|
+
|
|
32
44
|
self.empty_ok = empty_ok
|
|
33
45
|
|
|
34
46
|
if self.array.shape == (0,):
|
|
@@ -115,20 +115,36 @@ class MappedMatrix:
|
|
|
115
115
|
self.add_indexers(indexer_override, seed_override)
|
|
116
116
|
|
|
117
117
|
try:
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
empty_ok,
|
|
129
|
-
),
|
|
118
|
+
if row_mapper is None:
|
|
119
|
+
row_arrays = []
|
|
120
|
+
for obj in self.groups:
|
|
121
|
+
arr = obj.row_indices_for_mapping()
|
|
122
|
+
row_arrays.append(arr)
|
|
123
|
+
|
|
124
|
+
row_values = safe_concatenate_indices(row_arrays, empty_ok)
|
|
125
|
+
|
|
126
|
+
self.row_mapper = ArrayMapper(
|
|
127
|
+
array=row_values,
|
|
130
128
|
empty_ok=empty_ok,
|
|
131
129
|
)
|
|
130
|
+
else:
|
|
131
|
+
self.row_mapper = row_mapper
|
|
132
|
+
|
|
133
|
+
if not diagonal:
|
|
134
|
+
if col_mapper is None:
|
|
135
|
+
col_arrays = []
|
|
136
|
+
for obj in self.groups:
|
|
137
|
+
arr = obj.col_indices_for_mapping()
|
|
138
|
+
col_arrays.append(arr)
|
|
139
|
+
|
|
140
|
+
col_values = safe_concatenate_indices(col_arrays, empty_ok)
|
|
141
|
+
|
|
142
|
+
self.col_mapper = ArrayMapper(
|
|
143
|
+
array=col_values,
|
|
144
|
+
empty_ok=empty_ok,
|
|
145
|
+
)
|
|
146
|
+
else:
|
|
147
|
+
self.col_mapper = col_mapper
|
|
132
148
|
except AllArraysEmpty:
|
|
133
149
|
handle_all_arrays_empty(
|
|
134
150
|
packages=self.packages, matrix_label=self.matrix_label, identifier=identifier
|
|
@@ -234,7 +250,7 @@ class MappedMatrix:
|
|
|
234
250
|
obj.add_indexer(indexer=package.indexer)
|
|
235
251
|
else:
|
|
236
252
|
package.indexer = RandomIndexer(
|
|
237
|
-
seed=seed_override
|
|
253
|
+
seed=seed_override if seed_override is not None else package.metadata["seed"],
|
|
238
254
|
max_value=package.get_max_index_value(),
|
|
239
255
|
)
|
|
240
256
|
for obj in resources:
|
|
@@ -69,7 +69,9 @@ class ResourceGroup:
|
|
|
69
69
|
self.custom_filter = custom_filter
|
|
70
70
|
self.transpose = transpose
|
|
71
71
|
self.vector = self.is_vector()
|
|
72
|
-
self.seed =
|
|
72
|
+
self.seed = (
|
|
73
|
+
seed_override if seed_override is not None else self.package.metadata.get("seed")
|
|
74
|
+
)
|
|
73
75
|
|
|
74
76
|
if custom_filter is not None:
|
|
75
77
|
self.custom_filter_mask = custom_filter(self.get_indices_data())
|
|
@@ -218,11 +220,29 @@ class ResourceGroup:
|
|
|
218
220
|
|
|
219
221
|
def unique_row_indices_for_mapping(self):
|
|
220
222
|
"""Return array of unique indices that respect aggregation policy"""
|
|
221
|
-
|
|
223
|
+
data = mask_array(self.get_indices_data()["row"], self.custom_filter_mask)
|
|
224
|
+
# Structured-array field views can be non-contiguous; materialize contiguous memory first
|
|
225
|
+
# to avoid pathological performance in `np.unique`.
|
|
226
|
+
return np.unique(np.ascontiguousarray(data))
|
|
222
227
|
|
|
223
228
|
def unique_col_indices_for_mapping(self):
|
|
224
229
|
"""Return array of unique indices that respect aggregation policy"""
|
|
225
|
-
|
|
230
|
+
data = mask_array(self.get_indices_data()["col"], self.custom_filter_mask)
|
|
231
|
+
# Structured-array field views can be non-contiguous; materialize contiguous memory first
|
|
232
|
+
# to avoid pathological performance in `np.unique`.
|
|
233
|
+
return np.unique(np.ascontiguousarray(data))
|
|
234
|
+
|
|
235
|
+
def row_indices_for_mapping(self):
|
|
236
|
+
"""Return raw row indices for mapping, with custom filter applied if present."""
|
|
237
|
+
data = mask_array(self.get_indices_data()["row"], self.custom_filter_mask)
|
|
238
|
+
# Force contiguous memory to improve downstream sorting/uniquing performance.
|
|
239
|
+
return np.ascontiguousarray(data)
|
|
240
|
+
|
|
241
|
+
def col_indices_for_mapping(self):
|
|
242
|
+
"""Return raw column indices for mapping, with custom filter applied if present."""
|
|
243
|
+
data = mask_array(self.get_indices_data()["col"], self.custom_filter_mask)
|
|
244
|
+
# Force contiguous memory to improve downstream sorting/uniquing performance.
|
|
245
|
+
return np.ascontiguousarray(data)
|
|
226
246
|
|
|
227
247
|
def add_indexer(self, indexer: Indexer):
|
|
228
248
|
self.indexer = indexer
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
import pytest
|
|
3
3
|
|
|
4
|
+
import matrix_utils.array_mapper as array_mapper_module
|
|
4
5
|
from matrix_utils.array_mapper import ArrayMapper
|
|
5
6
|
from matrix_utils.errors import EmptyArray
|
|
6
7
|
|
|
@@ -78,3 +79,27 @@ def test_empty_array_ok():
|
|
|
78
79
|
am = ArrayMapper(array=np.array([], dtype=int), empty_ok=True)
|
|
79
80
|
assert am.array.shape == (0,)
|
|
80
81
|
assert am.empty_input
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_empty_mapper_maps_to_missing_values():
|
|
85
|
+
am = ArrayMapper(array=np.array([], dtype=int), empty_ok=True)
|
|
86
|
+
result = am.map_array(np.array([0, 42], dtype=int))
|
|
87
|
+
assert np.array_equal(result, np.array([-1, -1]))
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def test_reverse_dict():
|
|
91
|
+
am = ArrayMapper(array=np.array([10, 20, 10, 30]))
|
|
92
|
+
assert am.reverse_dict() == {0: 10, 1: 20, 2: 30}
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def test_large_integer_array_pandas_fallback(monkeypatch):
|
|
96
|
+
# Trigger the large integer optimization branch and force fallback
|
|
97
|
+
inpt = np.tile(np.array([5, 2, 7, 2, 5], dtype=np.int64), 25_000)
|
|
98
|
+
|
|
99
|
+
def raise_type_error(_):
|
|
100
|
+
raise TypeError("boom")
|
|
101
|
+
|
|
102
|
+
monkeypatch.setattr(array_mapper_module.pd, "unique", raise_type_error)
|
|
103
|
+
|
|
104
|
+
am = ArrayMapper(array=inpt)
|
|
105
|
+
assert np.array_equal(am.array, np.array([2, 5, 7]))
|
|
@@ -842,3 +842,29 @@ def test_empty_combinatorial_datapackage():
|
|
|
842
842
|
)
|
|
843
843
|
for _ in range(10):
|
|
844
844
|
next(mm)
|
|
845
|
+
|
|
846
|
+
|
|
847
|
+
def test_input_indexer_vector_raises_on_unsupported_type():
|
|
848
|
+
class BadIndexer:
|
|
849
|
+
index = "not-an-index"
|
|
850
|
+
|
|
851
|
+
def __next__(self):
|
|
852
|
+
return self
|
|
853
|
+
|
|
854
|
+
s = bwp.create_datapackage(sequential=True)
|
|
855
|
+
s.add_persistent_vector(
|
|
856
|
+
matrix="foo",
|
|
857
|
+
data_array=np.arange(3),
|
|
858
|
+
indices_array=np.array([(0, 0), (1, 1), (0, 1)], dtype=bwp.INDICES_DTYPE),
|
|
859
|
+
)
|
|
860
|
+
|
|
861
|
+
mm = MappedMatrix(
|
|
862
|
+
packages=[s],
|
|
863
|
+
matrix="foo",
|
|
864
|
+
use_arrays=False,
|
|
865
|
+
use_distributions=False,
|
|
866
|
+
indexer_override=BadIndexer(),
|
|
867
|
+
)
|
|
868
|
+
|
|
869
|
+
with pytest.raises(ValueError, match="Can't understand indexer value"):
|
|
870
|
+
mm.input_indexer_vector()
|
|
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
|
{matrix_utils-0.6.3 → matrix_utils-0.7.0}/dev/Real-time modification of hybrid databases.ipynb
RENAMED
|
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
|