Skip to content

Commit 3174527

Browse files
authored
Merge pull request #848 from pydata/ms/hits-example
Add HITS use case.
2 parents 851e82c + e6ae870 commit 3174527

10 files changed

+123
-8
lines changed

.github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ jobs:
6868
- name: Build and install Sparse
6969
run: |
7070
pip install -U setuptools wheel
71-
pip install '.[finch]' scipy
71+
pip install '.[finch]' scipy dask networkx graphblas-algorithms
7272
- name: Run examples
7373
run: ci/test_examples.sh
7474

ci/Finch-array-api-skips.txt

+2
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@ array_api_tests/test_searching_functions.py::test_nonzero_zerodim_error
44
array_api_tests/test_special_cases.py::test_unary[sign((x_i is -0 or x_i == +0)) -> 0]
55
# `broadcast_to` is not defined in Finch, hangs as xfail
66
array_api_tests/test_searching_functions.py::test_where
7+
# `test_solve` is not defined in Finch, hangs as xfail
8+
array_api_tests/test_linalg.py::test_solve

ci/Finch-array-api-xfails.txt

+32-2
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,17 @@ array_api_tests/test_signatures.py::test_extension_func_signature[linalg.pinv]
5959
array_api_tests/test_signatures.py::test_extension_func_signature[linalg.svdvals]
6060
array_api_tests/test_signatures.py::test_extension_func_signature[linalg.tensordot]
6161
array_api_tests/test_signatures.py::test_extension_func_signature[linalg.vecdot]
62-
array_api_tests/test_signatures.py::test_extension_func_signature[linalg.vector_norm]
62+
array_api_tests/test_signatures.py::test_extension_func_signature[linalg.det]
63+
array_api_tests/test_signatures.py::test_extension_func_signature[linalg.diagonal]
64+
array_api_tests/test_signatures.py::test_extension_func_signature[linalg.eigh]
65+
array_api_tests/test_signatures.py::test_extension_func_signature[linalg.eigvalsh]
66+
array_api_tests/test_signatures.py::test_extension_func_signature[linalg.inv]
67+
array_api_tests/test_signatures.py::test_extension_func_signature[linalg.matrix_power]
68+
array_api_tests/test_signatures.py::test_extension_func_signature[linalg.qr]
69+
array_api_tests/test_signatures.py::test_extension_func_signature[linalg.slogdet]
70+
array_api_tests/test_signatures.py::test_extension_func_signature[linalg.solve]
71+
array_api_tests/test_signatures.py::test_extension_func_signature[linalg.svd]
72+
array_api_tests/test_signatures.py::test_extension_func_signature[linalg.trace]
6373
# Array object namespace
6474
array_api_tests/test_signatures.py::test_array_method_signature[__dlpack__]
6575
array_api_tests/test_signatures.py::test_array_method_signature[__dlpack_device__]
@@ -116,7 +126,6 @@ array_api_tests/test_has_names.py::test_has_names[linalg-svdvals]
116126
array_api_tests/test_has_names.py::test_has_names[linalg-tensordot]
117127
array_api_tests/test_has_names.py::test_has_names[linalg-trace]
118128
array_api_tests/test_has_names.py::test_has_names[linalg-vecdot]
119-
array_api_tests/test_has_names.py::test_has_names[linalg-vector_norm]
120129
array_api_tests/test_has_names.py::test_has_names[statistical-cumulative_sum]
121130
array_api_tests/test_has_names.py::test_has_names[statistical-mean]
122131
array_api_tests/test_has_names.py::test_has_names[statistical-std]
@@ -173,6 +182,27 @@ array_api_tests/test_indexing_functions.py::test_take
173182
# not implemented
174183
array_api_tests/test_linalg.py::test_matrix_transpose
175184
array_api_tests/test_linalg.py::test_vecdot
185+
array_api_tests/test_linalg.py::test_eigh
186+
array_api_tests/test_linalg.py::test_eigvalsh
187+
array_api_tests/test_linalg.py::test_inv
188+
array_api_tests/test_linalg.py::test_linalg_matmul
189+
array_api_tests/test_linalg.py::test_matrix_norm
190+
array_api_tests/test_linalg.py::test_matrix_power
191+
array_api_tests/test_linalg.py::test_matrix_rank
192+
array_api_tests/test_linalg.py::test_linalg_matrix_transpose
193+
array_api_tests/test_linalg.py::test_outer
194+
array_api_tests/test_linalg.py::test_pinv
195+
array_api_tests/test_linalg.py::test_qr
196+
array_api_tests/test_linalg.py::test_slogdet
197+
array_api_tests/test_linalg.py::test_cholesky
198+
array_api_tests/test_linalg.py::test_det
199+
array_api_tests/test_linalg.py::test_diagonal
200+
array_api_tests/test_linalg.py::test_vector_norm
201+
array_api_tests/test_linalg.py::test_svdvals
202+
array_api_tests/test_linalg.py::test_svd
203+
array_api_tests/test_linalg.py::test_trace
204+
array_api_tests/test_linalg.py::test_linalg_vecdot
205+
array_api_tests/test_linalg.py::test_linalg_tensordot
176206

177207
# test_manipulation_functions
178208

ci/environment.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ dependencies:
66
- python
77
- pip
88
- pip:
9-
- finch-tensor>=0.2.4
9+
- finch-tensor>=0.2.8
1010
- finch-mlir>=0.0.2
1111
- pytest-codspeed
1212
- numpy

ci/test_examples.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env bash
22
set -euxo pipefail
33

4-
for example in $(find ./docs/examples/ -iname *.py); do
4+
for example in $(find ./examples/ -iname '*.py'); do
55
CI_MODE=1 python $example
66
done

examples/hits_example.py

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import os
2+
from typing import Any
3+
4+
import graphblas as gb
5+
import graphblas_algorithms as ga
6+
7+
import numpy as np
8+
import scipy.sparse as sps
9+
from numpy.testing import assert_allclose
10+
11+
os.environ["SPARSE_BACKEND"] = "Finch"
12+
import sparse
13+
14+
# select namespace
15+
xp = sparse # np jnp
16+
Array = Any
17+
18+
19+
def converged(xprev: Array, x: Array, N: int, tol: float) -> bool:
20+
err = xp.sum(xp.abs(x - xprev))
21+
return err < xp.asarray(N * tol)
22+
23+
24+
class Graph:
25+
def __init__(self, A: Array):
26+
assert A.ndim == 2 and A.shape[0] == A.shape[1]
27+
self.N = A.shape[0]
28+
self.A = A
29+
30+
31+
@sparse.compiled()
32+
def kernel(hprev: Array, A: Array, N: int, tol: float) -> tuple[Array, Array, Array]:
33+
a = hprev.mT @ A
34+
h = A @ a.mT
35+
h = h / xp.max(h)
36+
conv = converged(hprev, h, N, tol)
37+
return h, a, conv
38+
39+
40+
def hits_finch(G: Graph, max_iter: int = 100, tol: float = 1e-8, normalized: bool = True) -> tuple[Array, Array]:
41+
N = G.N
42+
if N == 0:
43+
return xp.asarray([]), xp.asarray([])
44+
45+
h = xp.full((N, 1), 1.0 / N)
46+
A = xp.asarray(G.A)
47+
48+
for _ in range(max_iter):
49+
hprev = h
50+
a = hprev.mT @ A
51+
h = A @ a.mT
52+
h = h / xp.max(h)
53+
if converged(hprev, h, N, tol):
54+
break
55+
# alternatively these lines can be compiled
56+
# h, a, conv = kernel(h, A, N, tol)
57+
else:
58+
raise Exception("Didn't converge")
59+
60+
if normalized:
61+
h = h / xp.sum(xp.abs(h))
62+
a = a / xp.sum(xp.abs(a))
63+
return h, a
64+
65+
66+
if __name__ == "__main__":
67+
coords = (np.array([0, 0, 1, 2, 2, 3]), np.array([1, 3, 0, 0, 1, 2]))
68+
data = np.array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0])
69+
A = sps.coo_array((data, coords))
70+
G = Graph(A)
71+
72+
h_finch, a_finch = hits_finch(G)
73+
74+
print(h_finch, a_finch)
75+
76+
M = gb.io.from_scipy_sparse(A)
77+
G = ga.Graph(M)
78+
h_gb, a_gb = ga.hits(G)
79+
80+
assert_allclose(h_finch.todense().ravel(), h_gb.to_dense())
81+
assert_allclose(a_finch.todense().ravel(), a_gb.to_dense())

examples/triangles_example.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
@sparse.compiled()
2727
def count_triangles_finch(a):
28-
return sparse.sum(a @ a * a) / 6
28+
return sparse.sum(a @ a * a) / sparse.asarray(6)
2929

3030
# Compile & Benchmark
3131
result_finch = benchmark(count_triangles_finch, args=[a], info="Finch", iters=ITERS)

pixi.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ juliaup = ">=1.17.10"
5555

5656
[feature.finch.pypi-dependencies]
5757
scipy = ">=1.13"
58-
finch-tensor = ">=0.2.4"
58+
finch-tensor = ">=0.2.8"
5959

6060
[feature.finch.activation.env]
6161
SPARSE_BACKEND = "Finch"

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ tests = [
5454
tox = ["sparse[tests]", "tox"]
5555
notebooks = ["sparse[tests]", "nbmake", "matplotlib"]
5656
all = ["sparse[docs,tox,notebooks,mlir]", "matrepr"]
57-
finch = ["finch-tensor>=0.2.4"]
57+
finch = ["finch-tensor>=0.2.8"]
5858
mlir = ["finch-mlir>=0.0.2"]
5959

6060
[project.urls]

sparse/finch_backend/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
lazy,
7070
less,
7171
less_equal,
72+
linalg,
7273
linspace,
7374
log,
7475
log1p,
@@ -161,6 +162,7 @@
161162
"int64",
162163
"int_",
163164
"lazy",
165+
"linalg",
164166
"matmul",
165167
"multiply",
166168
"negative",

0 commit comments

Comments
 (0)