From 20a8b1bfaf96d977cfb994ec53fc29a1606e82dd Mon Sep 17 00:00:00 2001 From: Dan Schult Date: Mon, 12 May 2025 01:57:07 -0400 Subject: [PATCH 1/5] migration guide pass 1: make code work for both spmatrix and sparray switch from isspmatrix_csr to issparse and format='csr' --- libpysal/weights/raster.py | 2 +- libpysal/weights/set_operations.py | 6 +++--- libpysal/weights/util.py | 10 +++++----- libpysal/weights/weights.py | 8 ++++---- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/libpysal/weights/raster.py b/libpysal/weights/raster.py index ee373609d..ec895a851 100644 --- a/libpysal/weights/raster.py +++ b/libpysal/weights/raster.py @@ -270,7 +270,7 @@ def da2WSP( # then eliminate zeros from the data. This changes the # sparsity of the csr_matrix !! if k > 1 and not include_nodata: - sw = sum(sw**x for x in range(1, k + 1)) + sw = sum(sparse.linalg.matrix_power(sw, x) for x in range(1, k + 1)) sw.setdiag(0) sw.eliminate_zeros() sw.data[:] = np.ones_like(sw.data, dtype=np.int8) diff --git a/libpysal/weights/set_operations.py b/libpysal/weights/set_operations.py index 2bf3d6037..2b72dcf7f 100644 --- a/libpysal/weights/set_operations.py +++ b/libpysal/weights/set_operations.py @@ -12,7 +12,7 @@ import copy from numpy import ones -from scipy.sparse import isspmatrix_csr +from scipy.sparse import issparse from .weights import WSP, W @@ -501,9 +501,9 @@ def w_clip(w1, w2, outSP=True, **kwargs): # noqa: N803 if not w1.id_order: w1.id_order = None id_order = w1.id_order - if not isspmatrix_csr(w1): + if not issparse(w1): w1 = w1.sparse - if not isspmatrix_csr(w2): + if not issparse(w2): w2 = w2.sparse w2.data = ones(w2.data.shape) wc = w1.multiply(w2) diff --git a/libpysal/weights/util.py b/libpysal/weights/util.py index 6154387f1..079ce8bbb 100644 --- a/libpysal/weights/util.py +++ b/libpysal/weights/util.py @@ -511,7 +511,7 @@ def higher_order_sp( w = w.sparse else: raise ValueError("Weights are not binary (0,1)") - elif scipy.sparse.isspmatrix_csr(w): + elif scipy.sparse.issparse(w) and w.format == "csr": if not np.unique(w.data) == np.array([1.0]): raise ValueError( "Sparse weights matrix is not binary (0,1) weights matrix." @@ -523,17 +523,17 @@ def higher_order_sp( ) if lower_order: - wk = sum(w**x for x in range(1, k + 1)) + wk = sum(sparse.linalg.matrix_power(w, k) for k in range(1, k+1)) shortest_path = False else: - wk = w**k + wk = sparse.linalg.matrix_power(w, k) rk, ck = wk.nonzero() sk = set(zip(rk, ck, strict=True)) if shortest_path: for j in range(1, k): - wj = w**j + wj = sparse.linalg.matrix_power(w, j) rj, cj = wj.nonzero() sj = set(zip(rj, cj, strict=True)) sk.difference_update(sj) @@ -1225,7 +1225,7 @@ def lat2SW(nrows=3, ncols=5, criterion="rook", row_st=False): m = sparse.dia_matrix((data, offsets), shape=(n, n), dtype=np.int8) m = m + m.T if row_st: - m = sparse.spdiags(1.0 / m.sum(1).T, 0, *m.shape) * m + m = sparse.dia_matrix(((1.0 / m.sum(1).T), [0]), shape=m.shape) @ m m = m.tocsc() m.eliminate_zeros() return m diff --git a/libpysal/weights/weights.py b/libpysal/weights/weights.py index 8e582953f..9c125f1d7 100644 --- a/libpysal/weights/weights.py +++ b/libpysal/weights/weights.py @@ -721,7 +721,7 @@ def diagW2(self): trcW2 """ if "diagw2" not in self._cache: - self._diagW2 = (self.sparse * self.sparse).diagonal() + self._diagW2 = (self.sparse @ self.sparse).diagonal() self._cache["diagW2"] = self._diagW2 return self._diagW2 @@ -734,7 +734,7 @@ def diagWtW(self): trcWtW """ if "diagWtW" not in self._cache: - self._diagWtW = (self.sparse.transpose() * self.sparse).diagonal() + self._diagWtW = (self.sparse.transpose() @ self.sparse).diagonal() self._cache["diagWtW"] = self._diagWtW return self._diagWtW @@ -757,7 +757,7 @@ def diagWtW_WW(self): if "diagWtW_WW" not in self._cache: wt = self.sparse.transpose() w = self.sparse - self._diagWtW_WW = (wt * w + w * w).diagonal() + self._diagWtW_WW = (wt @ w + w @ w).diagonal() self._cache["diagWtW_WW"] = self._diagWtW_WW return self._diagWtW_WW @@ -1603,7 +1603,7 @@ def diagWtW_WW(self): if "diagWtW_WW" not in self._cache: wt = self.sparse.transpose() w = self.sparse - self._diagWtW_WW = (wt * w + w * w).diagonal() + self._diagWtW_WW = (wt @ w + w @ w).diagonal() self._cache["diagWtW_WW"] = self._diagWtW_WW return self._diagWtW_WW From fa7d20069d7d42634fa88eca4fb665807b4b532a Mon Sep 17 00:00:00 2001 From: Dan Schult Date: Mon, 12 May 2025 10:27:16 -0400 Subject: [PATCH 2/5] add conftest.txt to monkey-patch scipy sparse to fail on breaking * and ** --- libpysal/conftest.txt | 153 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 libpysal/conftest.txt diff --git a/libpysal/conftest.txt b/libpysal/conftest.txt new file mode 100644 index 000000000..15ca78acd --- /dev/null +++ b/libpysal/conftest.txt @@ -0,0 +1,153 @@ +# A .txt file that when renamed conftest.py will allow pytest to flag sparray troubles. +#================== Added to check spmatrix usage ======================== +import scipy + +def flag_this_call(*args, **kwds): + raise ValueError("Old spmatrix version of bmat called") + +scipy.sparse._construct.bmat = flag_this_call +scipy.sparse._construct.rand = flag_this_call +scipy.sparse._construct.rand = flag_this_call + +class _strict_mul_mixin: + def __mul__(self, other): + if not scipy.sparse._sputils.isscalarlike(other): + raise ValueError('Operator * used here! Change to @?') + return super().__mul__(other) + + def __rmul__(self, other): + if not scipy.sparse._sputils.isscalarlike(other): + raise ValueError('Operator * used here! Change to @?') + return super().__rmul__(other) + + def __imul__(self, other): + if not scipy.sparse._sputils.isscalarlike(other): + raise ValueError('Operator * used here! Change to @?') + return super().__imul__(other) + + def __pow__(self, *args, **kwargs): + raise ValueError('spmatrix ** found! Use linalg.matrix_power?') + + @property + def A(self): + raise TypeError('spmatrix A property found! Use .toarray()') + + @property + def H(self): + raise TypeError('spmatrix H property found! Use .conjugate().T') + + def asfptype(self): + raise TypeError('spmatrix asfptype found! rewrite needed') + + def get_shape(self): + raise TypeError('spmatrix get_shape found! Use .shape') + + def getformat(self): + raise TypeError('spmatrix getformat found! Use .shape') + + def getmaxprint(self): + raise TypeError('spmatrix getmaxprint found! Use .shape') + + def getnnz(self): + raise TypeError('spmatrix getnnz found! Use .shape') + + def getH(self): + raise TypeError('spmatrix getH found! Use .shape') + + def getrow(self): + raise TypeError('spmatrix getrow found! Use .shape') + + def getcol(self): + raise TypeError('spmatrix getcol found! Use .shape') + + def sum(self, *args, axis=None, **kwds): + if axis is not None: + raise TypeError(f'spmatrix sum found using axis={axis}!') + return super().sum(*args, **kwds) + + def sum(self, *args, axis=None, **kwds): + if axis is not None: + raise TypeError(f'spmatrix sum found using axis={axis}!') + return super().sum(*args, **kwds) + + def mean(self, *args, axis=None, **kwds): + if axis is not None: + raise TypeError(f'spmatrix sum found using axis={axis}!') + return super().sum(*args, **kwds) + + def min(self, *args, axis=None, **kwds): + if axis is not None: + raise TypeError(f'spmatrix sum found using axis={axis}!') + return super().sum(*args, **kwds) + + def max(self, *args, axis=None, **kwds): + if axis is not None: + raise TypeError(f'spmatrix sum found using axis={axis}!') + return super().sum(*args, **kwds) + + def argmin(self, *args, axis=None, **kwds): + if axis is not None: + raise TypeError(f'spmatrix sum found using axis={axis}!') + return super().sum(*args, **kwds) + + def argmax(self, *args, axis=None, **kwds): + if axis is not None: + raise TypeError(f'spmatrix sum found using axis={axis}!') + return super().sum(*args, **kwds) + + +class _strict_coo_matrix(_strict_mul_mixin, scipy.sparse.coo_matrix): + pass + +class _strict_bsr_matrix(_strict_mul_mixin, scipy.sparse.bsr_matrix): + pass + +class _strict_csr_matrix(_strict_mul_mixin, scipy.sparse.csr_matrix): + pass + +class _strict_csc_matrix(_strict_mul_mixin, scipy.sparse.csc_matrix): + pass + +class _strict_dok_matrix(_strict_mul_mixin, scipy.sparse.dok_matrix): + pass + +class _strict_lil_matrix(_strict_mul_mixin, scipy.sparse.lil_matrix): + pass + +class _strict_dia_matrix(_strict_mul_mixin, scipy.sparse.dia_matrix): + pass + +scipy.sparse.coo_matrix = scipy.sparse._coo.coo_matrix = _strict_coo_matrix +scipy.sparse.bsr_matrix = scipy.sparse._bsr.bsr_matrix = _strict_bsr_matrix +scipy.sparse.csr_matrix = scipy.sparse._csr.csr_matrix = _strict_csr_matrix +scipy.sparse.csc_matrix = scipy.sparse._csc.csc_matrix = _strict_csc_matrix +scipy.sparse.dok_matrix = scipy.sparse._dok.dok_matrix = _strict_dok_matrix +scipy.sparse.lil_matrix = scipy.sparse._lil.lil_matrix = _strict_lil_matrix +scipy.sparse.dia_matrix = scipy.sparse._dia.dia_matrix = _strict_dia_matrix + +scipy.sparse._compressed.csr_matrix = _strict_csr_matrix + +scipy.sparse._construct.bsr_matrix = _strict_bsr_matrix +scipy.sparse._construct.coo_matrix = _strict_coo_matrix +scipy.sparse._construct.csc_matrix = _strict_csc_matrix +scipy.sparse._construct.csr_matrix = _strict_csr_matrix +scipy.sparse._construct.dia_matrix = _strict_dia_matrix + +scipy.sparse._extract.coo_matrix = _strict_coo_matrix + +scipy.sparse._matrix.bsr_matrix = _strict_bsr_matrix +scipy.sparse._matrix.coo_matrix = _strict_coo_matrix +scipy.sparse._matrix.csc_matrix = _strict_csc_matrix +scipy.sparse._matrix.csr_matrix = _strict_csr_matrix +scipy.sparse._matrix.dia_matrix = _strict_dia_matrix +scipy.sparse._matrix.dok_matrix = _strict_dok_matrix +scipy.sparse._matrix.lil_matrix = _strict_lil_matrix + +del _strict_coo_matrix +del _strict_bsr_matrix +del _strict_csr_matrix +del _strict_csc_matrix +del _strict_dok_matrix +del _strict_lil_matrix +del _strict_dia_matrix +#========================================== From c2054e5219719d2da50ee5ed80d71a6a0978a8ff Mon Sep 17 00:00:00 2001 From: Dan Schult Date: Tue, 13 May 2025 12:54:47 -0400 Subject: [PATCH 3/5] add test of row_st code to help coverage bot --- libpysal/weights/tests/test_util.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/libpysal/weights/tests/test_util.py b/libpysal/weights/tests/test_util.py index 9d1b7208e..6d7baa556 100644 --- a/libpysal/weights/tests/test_util.py +++ b/libpysal/weights/tests/test_util.py @@ -25,13 +25,20 @@ def test_lat2_w(self): assert w9[0] == {1: 1.0, 3: 1.0} assert w9[3] == {0: 1.0, 4: 1.0, 6: 1.0} - def test_lat2_sw(self): - w9 = util.lat2SW(3, 3) + @pytest.mark.parametrize("row_st", [True, False]) + def test_lat2_sw(self, row_st): + w9 = util.lat2SW(3, 3, row_st=row_st) rows, cols = w9.shape n = rows * cols assert w9.nnz == 24 pct_nonzero = w9.nnz / float(n) assert pct_nonzero == 0.29629629629629628 + if row_st: + # these 3 lines can be replaced when scipy >=1.15 is assured. + # w9 = (w9.T.multiply(w9.tocsr().count_nonzero(axis=1))).T + w9csr = w9.tocsr() + nnz_by_axis1 = np.diff(w9csr.indptr) + w9 = (w9csr.T.multiply(nnz_by_axis1)).T data = w9.todense().tolist() assert data[0] == [0, 1, 0, 1, 0, 0, 0, 0, 0] assert data[1] == [1, 0, 1, 0, 1, 0, 0, 0, 0] From 760c6e4936d44d81d9a30a5704457d34b03ea5ba Mon Sep 17 00:00:00 2001 From: Dan Schult Date: Tue, 13 May 2025 15:00:40 -0400 Subject: [PATCH 4/5] workaround scipy1.8 limitation. Could be reverted for scipy1.12 --- libpysal/weights/raster.py | 8 +++++++- libpysal/weights/util.py | 31 ++++++++++++++++++++++++++++--- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/libpysal/weights/raster.py b/libpysal/weights/raster.py index ec895a851..702e10c7e 100644 --- a/libpysal/weights/raster.py +++ b/libpysal/weights/raster.py @@ -270,7 +270,13 @@ def da2WSP( # then eliminate zeros from the data. This changes the # sparsity of the csr_matrix !! if k > 1 and not include_nodata: - sw = sum(sparse.linalg.matrix_power(sw, x) for x in range(1, k + 1)) + #### Could be as follows after scipy >=1.12 is required + # sw = sum(sparse.linalg.matrix_power(sw, x) for x in range(1, k + 1)) + tmp = sw.copy() + for _ in range(k - 1): + tmp += tmp @ sw + sw + sw = tmp + #### sw.setdiag(0) sw.eliminate_zeros() sw.data[:] = np.ones_like(sw.data, dtype=np.int8) diff --git a/libpysal/weights/util.py b/libpysal/weights/util.py index 079ce8bbb..12a4a13af 100644 --- a/libpysal/weights/util.py +++ b/libpysal/weights/util.py @@ -523,17 +523,42 @@ def higher_order_sp( ) if lower_order: - wk = sum(sparse.linalg.matrix_power(w, k) for k in range(1, k+1)) shortest_path = False + #### Could be as follows after scipy >=1.12 is required + # wk = sum(sparse.linalg.matrix_power(w, k) for k in range(1, k+1)) + wk = w.copy() + for _ in range(k - 1): + wk = wk @ w + w + #### else: - wk = sparse.linalg.matrix_power(w, k) + #### Could be as follows after scipy >=1.12 is required + # wk = sparse.linalg.matrix_power(w, k) + wk = w.copy() + x = 1 + while 2 * x < k: + wk = wk @ wk + x *= 2 + while x < k: + wk = wk @ w + x += 1 + #### rk, ck = wk.nonzero() sk = set(zip(rk, ck, strict=True)) if shortest_path: for j in range(1, k): - wj = sparse.linalg.matrix_power(w, j) + #### Could be as follows after scipy >=1.12 is required + # wj = sparse.linalg.matrix_power(w, j) + wj = w.copy() + x = 1 + while 2 * x < j: + wj = wj @ wj + x *= 2 + while x < j: + wj = wj @ w + x += 1 + #### rj, cj = wj.nonzero() sj = set(zip(rj, cj, strict=True)) sk.difference_update(sj) From 8881bf4b8c28178120849259f0beb7acd5e581be Mon Sep 17 00:00:00 2001 From: Dan Schult Date: Wed, 14 May 2025 13:35:05 -0400 Subject: [PATCH 5/5] Clean up comments and remove conftest.txt -- PR ready to remove Draft status --- libpysal/conftest.txt | 153 ---------------------------- libpysal/weights/raster.py | 2 +- libpysal/weights/tests/test_util.py | 3 +- libpysal/weights/util.py | 6 +- 4 files changed, 6 insertions(+), 158 deletions(-) delete mode 100644 libpysal/conftest.txt diff --git a/libpysal/conftest.txt b/libpysal/conftest.txt deleted file mode 100644 index 15ca78acd..000000000 --- a/libpysal/conftest.txt +++ /dev/null @@ -1,153 +0,0 @@ -# A .txt file that when renamed conftest.py will allow pytest to flag sparray troubles. -#================== Added to check spmatrix usage ======================== -import scipy - -def flag_this_call(*args, **kwds): - raise ValueError("Old spmatrix version of bmat called") - -scipy.sparse._construct.bmat = flag_this_call -scipy.sparse._construct.rand = flag_this_call -scipy.sparse._construct.rand = flag_this_call - -class _strict_mul_mixin: - def __mul__(self, other): - if not scipy.sparse._sputils.isscalarlike(other): - raise ValueError('Operator * used here! Change to @?') - return super().__mul__(other) - - def __rmul__(self, other): - if not scipy.sparse._sputils.isscalarlike(other): - raise ValueError('Operator * used here! Change to @?') - return super().__rmul__(other) - - def __imul__(self, other): - if not scipy.sparse._sputils.isscalarlike(other): - raise ValueError('Operator * used here! Change to @?') - return super().__imul__(other) - - def __pow__(self, *args, **kwargs): - raise ValueError('spmatrix ** found! Use linalg.matrix_power?') - - @property - def A(self): - raise TypeError('spmatrix A property found! Use .toarray()') - - @property - def H(self): - raise TypeError('spmatrix H property found! Use .conjugate().T') - - def asfptype(self): - raise TypeError('spmatrix asfptype found! rewrite needed') - - def get_shape(self): - raise TypeError('spmatrix get_shape found! Use .shape') - - def getformat(self): - raise TypeError('spmatrix getformat found! Use .shape') - - def getmaxprint(self): - raise TypeError('spmatrix getmaxprint found! Use .shape') - - def getnnz(self): - raise TypeError('spmatrix getnnz found! Use .shape') - - def getH(self): - raise TypeError('spmatrix getH found! Use .shape') - - def getrow(self): - raise TypeError('spmatrix getrow found! Use .shape') - - def getcol(self): - raise TypeError('spmatrix getcol found! Use .shape') - - def sum(self, *args, axis=None, **kwds): - if axis is not None: - raise TypeError(f'spmatrix sum found using axis={axis}!') - return super().sum(*args, **kwds) - - def sum(self, *args, axis=None, **kwds): - if axis is not None: - raise TypeError(f'spmatrix sum found using axis={axis}!') - return super().sum(*args, **kwds) - - def mean(self, *args, axis=None, **kwds): - if axis is not None: - raise TypeError(f'spmatrix sum found using axis={axis}!') - return super().sum(*args, **kwds) - - def min(self, *args, axis=None, **kwds): - if axis is not None: - raise TypeError(f'spmatrix sum found using axis={axis}!') - return super().sum(*args, **kwds) - - def max(self, *args, axis=None, **kwds): - if axis is not None: - raise TypeError(f'spmatrix sum found using axis={axis}!') - return super().sum(*args, **kwds) - - def argmin(self, *args, axis=None, **kwds): - if axis is not None: - raise TypeError(f'spmatrix sum found using axis={axis}!') - return super().sum(*args, **kwds) - - def argmax(self, *args, axis=None, **kwds): - if axis is not None: - raise TypeError(f'spmatrix sum found using axis={axis}!') - return super().sum(*args, **kwds) - - -class _strict_coo_matrix(_strict_mul_mixin, scipy.sparse.coo_matrix): - pass - -class _strict_bsr_matrix(_strict_mul_mixin, scipy.sparse.bsr_matrix): - pass - -class _strict_csr_matrix(_strict_mul_mixin, scipy.sparse.csr_matrix): - pass - -class _strict_csc_matrix(_strict_mul_mixin, scipy.sparse.csc_matrix): - pass - -class _strict_dok_matrix(_strict_mul_mixin, scipy.sparse.dok_matrix): - pass - -class _strict_lil_matrix(_strict_mul_mixin, scipy.sparse.lil_matrix): - pass - -class _strict_dia_matrix(_strict_mul_mixin, scipy.sparse.dia_matrix): - pass - -scipy.sparse.coo_matrix = scipy.sparse._coo.coo_matrix = _strict_coo_matrix -scipy.sparse.bsr_matrix = scipy.sparse._bsr.bsr_matrix = _strict_bsr_matrix -scipy.sparse.csr_matrix = scipy.sparse._csr.csr_matrix = _strict_csr_matrix -scipy.sparse.csc_matrix = scipy.sparse._csc.csc_matrix = _strict_csc_matrix -scipy.sparse.dok_matrix = scipy.sparse._dok.dok_matrix = _strict_dok_matrix -scipy.sparse.lil_matrix = scipy.sparse._lil.lil_matrix = _strict_lil_matrix -scipy.sparse.dia_matrix = scipy.sparse._dia.dia_matrix = _strict_dia_matrix - -scipy.sparse._compressed.csr_matrix = _strict_csr_matrix - -scipy.sparse._construct.bsr_matrix = _strict_bsr_matrix -scipy.sparse._construct.coo_matrix = _strict_coo_matrix -scipy.sparse._construct.csc_matrix = _strict_csc_matrix -scipy.sparse._construct.csr_matrix = _strict_csr_matrix -scipy.sparse._construct.dia_matrix = _strict_dia_matrix - -scipy.sparse._extract.coo_matrix = _strict_coo_matrix - -scipy.sparse._matrix.bsr_matrix = _strict_bsr_matrix -scipy.sparse._matrix.coo_matrix = _strict_coo_matrix -scipy.sparse._matrix.csc_matrix = _strict_csc_matrix -scipy.sparse._matrix.csr_matrix = _strict_csr_matrix -scipy.sparse._matrix.dia_matrix = _strict_dia_matrix -scipy.sparse._matrix.dok_matrix = _strict_dok_matrix -scipy.sparse._matrix.lil_matrix = _strict_lil_matrix - -del _strict_coo_matrix -del _strict_bsr_matrix -del _strict_csr_matrix -del _strict_csc_matrix -del _strict_dok_matrix -del _strict_lil_matrix -del _strict_dia_matrix -#========================================== diff --git a/libpysal/weights/raster.py b/libpysal/weights/raster.py index 702e10c7e..973a9ef17 100644 --- a/libpysal/weights/raster.py +++ b/libpysal/weights/raster.py @@ -270,7 +270,7 @@ def da2WSP( # then eliminate zeros from the data. This changes the # sparsity of the csr_matrix !! if k > 1 and not include_nodata: - #### Could be as follows after scipy >=1.12 is required + #### Can be this one-liner after scipy >=1.12 is assured # sw = sum(sparse.linalg.matrix_power(sw, x) for x in range(1, k + 1)) tmp = sw.copy() for _ in range(k - 1): diff --git a/libpysal/weights/tests/test_util.py b/libpysal/weights/tests/test_util.py index 6d7baa556..247b5aa10 100644 --- a/libpysal/weights/tests/test_util.py +++ b/libpysal/weights/tests/test_util.py @@ -34,11 +34,12 @@ def test_lat2_sw(self, row_st): pct_nonzero = w9.nnz / float(n) assert pct_nonzero == 0.29629629629629628 if row_st: - # these 3 lines can be replaced when scipy >=1.15 is assured. + #### Can be this one-liner after scipy >=1.15 is assured # w9 = (w9.T.multiply(w9.tocsr().count_nonzero(axis=1))).T w9csr = w9.tocsr() nnz_by_axis1 = np.diff(w9csr.indptr) w9 = (w9csr.T.multiply(nnz_by_axis1)).T + #### data = w9.todense().tolist() assert data[0] == [0, 1, 0, 1, 0, 0, 0, 0, 0] assert data[1] == [1, 0, 1, 0, 1, 0, 0, 0, 0] diff --git a/libpysal/weights/util.py b/libpysal/weights/util.py index 12a4a13af..e436035a9 100644 --- a/libpysal/weights/util.py +++ b/libpysal/weights/util.py @@ -524,14 +524,14 @@ def higher_order_sp( if lower_order: shortest_path = False - #### Could be as follows after scipy >=1.12 is required + #### Can be this one-liner after scipy >=1.12 is assured # wk = sum(sparse.linalg.matrix_power(w, k) for k in range(1, k+1)) wk = w.copy() for _ in range(k - 1): wk = wk @ w + w #### else: - #### Could be as follows after scipy >=1.12 is required + #### Can be this one-liner after scipy >=1.12 is assured # wk = sparse.linalg.matrix_power(w, k) wk = w.copy() x = 1 @@ -548,7 +548,7 @@ def higher_order_sp( if shortest_path: for j in range(1, k): - #### Could be as follows after scipy >=1.12 is required + #### Can be this one-liner after scipy >=1.12 is assured # wj = sparse.linalg.matrix_power(w, j) wj = w.copy() x = 1