Skip to content

Commit e3204d0

Browse files
committed
ENH: Update MLIR backend to LLVM 20.dev
1 parent cc3c8d9 commit e3204d0

File tree

10 files changed

+84
-57
lines changed

10 files changed

+84
-57
lines changed

.github/workflows/ci.yml

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,20 @@ jobs:
1111
strategy:
1212
matrix:
1313
os: ['ubuntu-latest']
14-
python: ['3.10', '3.11', '3.12']
14+
python: ['3.10'] # , '3.11', '3.12'
1515
pip_opts: ['']
1616
numba_boundscheck: [0]
17-
include:
18-
- os: macos-latest
19-
python: '3.10'
20-
- os: windows-latest
21-
python: '3.10'
22-
- os: ubuntu-latest
23-
python: '3.10'
24-
numba_boundscheck: 1
25-
- os: ubuntu-latest
26-
python: '3.10'
27-
pip_opts: 'numpy<2'
17+
# include:
18+
# - os: macos-latest
19+
# python: '3.10'
20+
# - os: windows-latest
21+
# python: '3.10'
22+
# - os: ubuntu-latest
23+
# python: '3.10'
24+
# numba_boundscheck: 1
25+
# - os: ubuntu-latest
26+
# python: '3.10'
27+
# pip_opts: 'numpy<2'
2828
fail-fast: false
2929
runs-on: ${{ matrix.os }}
3030
env:

ci/environment.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ dependencies:
1212
- pytest
1313
- pytest-cov
1414
- pytest-xdist
15-
- mlir-python-bindings==19.*
1615
- pip:
1716
- finch-tensor >=0.1.31
1817
- pytest-codspeed
18+
- https://github.com/nullplay/Finch-mlir/releases/download/latest/mlir_finch-0.0.1-cp310-cp310-linux_x86_64.whl

sparse/mlir_backend/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
try:
2-
import mlir # noqa: F401
2+
import mlir_finch # noqa: F401
33

4-
del mlir
4+
del mlir_finch
55
except ModuleNotFoundError as e:
66
raise ImportError(
77
"MLIR Python bindings not installed. Run "

sparse/mlir_backend/_common.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import functools
33
import weakref
44

5-
import mlir.runtime as rt
5+
import mlir_finch.runtime as rt
66

77
import numpy as np
88

sparse/mlir_backend/_conversions.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -94,13 +94,15 @@ def _from_scipy(arr: ScipySparseArray, copy: bool | None = None) -> Array:
9494
case "coo":
9595
if copy is not None and not copy:
9696
raise RuntimeError(f"`scipy.sparse.{type(arr.__name__)}` cannot be zero-copy converted.")
97-
coords = np.stack([arr.row, arr.col], axis=1)
97+
row, col = arr.row, arr.col
9898
pos = np.array([0, arr.nnz], dtype=np.int64)
9999
pos_width = pos.dtype.itemsize * 8
100-
crd_width = coords.dtype.itemsize * 8
100+
crd_width = row.dtype.itemsize * 8
101101
data = arr.data
102102
if copy:
103-
data = arr.data.copy()
103+
data = data.copy()
104+
row = row.copy()
105+
col = col.copy()
104106

105107
level_props = LevelProperties(0)
106108
if not arr.has_canonical_format:
@@ -109,15 +111,15 @@ def _from_scipy(arr: ScipySparseArray, copy: bool | None = None) -> Array:
109111
coo_format = get_storage_format(
110112
levels=(
111113
Level(LevelFormat.Compressed, level_props | LevelProperties.NonUnique),
112-
Level(LevelFormat.Singleton, level_props),
114+
Level(LevelFormat.Singleton, level_props | LevelProperties.SOA),
113115
),
114116
order=(0, 1),
115117
pos_width=pos_width,
116118
crd_width=crd_width,
117119
dtype=arr.dtype,
118120
)
119121

120-
return from_constituent_arrays(format=coo_format, arrays=(pos, coords, data), shape=arr.shape)
122+
return from_constituent_arrays(format=coo_format, arrays=(pos, row, col, data), shape=arr.shape)
121123
case _:
122124
raise NotImplementedError(f"No conversion implemented for `scipy.sparse.{type(arr.__name__)}`.")
123125

@@ -133,8 +135,8 @@ def to_scipy(arr: Array) -> ScipySparseArray:
133135
return sps.csr_array((data, indices, indptr), shape=arr.shape)
134136
return sps.csc_array((data, indices, indptr), shape=arr.shape)
135137
case (Level(LevelFormat.Compressed, _), Level(LevelFormat.Singleton, _)):
136-
_, coords, data = arr.get_constituent_arrays()
137-
return sps.coo_array((data, (coords[:, 0], coords[:, 1])), shape=arr.shape)
138+
_, row, col, data = arr.get_constituent_arrays()
139+
return sps.coo_array((data, (row, col)), shape=arr.shape)
138140
case _:
139141
raise RuntimeError(f"No conversion implemented for `{storage_format=}`.")
140142

sparse/mlir_backend/_core.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,23 @@
22
import ctypes.util
33
import os
44
import pathlib
5+
import sys
56

6-
from mlir.ir import Context
7-
from mlir.passmanager import PassManager
7+
from mlir_finch.ir import Context
8+
from mlir_finch.passmanager import PassManager
89

910
DEBUG = bool(int(os.environ.get("DEBUG", "0")))
1011
CWD = pathlib.Path(".")
1112

13+
finch_lib_path = f"{sys.prefix}/lib/python3.{sys.version_info.minor}/site-packages/lib"
14+
15+
ld_library_path = os.environ.get("LD_LIBRARY_PATH")
16+
ld_library_path = f"{finch_lib_path}:{ld_library_path}" if ld_library_path is None else finch_lib_path
17+
os.environ["LD_LIBRARY_PATH"] = ld_library_path
18+
1219
MLIR_C_RUNNER_UTILS = ctypes.util.find_library("mlir_c_runner_utils")
20+
if os.name == "posix":
21+
MLIR_C_RUNNER_UTILS = f"{finch_lib_path}/{MLIR_C_RUNNER_UTILS}"
1322
libc = ctypes.CDLL(ctypes.util.find_library("c")) if os.name != "nt" else ctypes.cdll.msvcrt
1423
libc.free.argtypes = [ctypes.c_void_p]
1524
libc.free.restype = None

sparse/mlir_backend/_dtypes.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
import math
44
import sys
55

6-
import mlir.runtime as rt
7-
from mlir import ir
6+
import mlir_finch.runtime as rt
7+
from mlir_finch import ir
88

99
import numpy as np
1010

sparse/mlir_backend/_ops.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import ctypes
22

3-
import mlir.execution_engine
4-
import mlir.passmanager
5-
from mlir import ir
6-
from mlir.dialects import arith, complex, func, linalg, sparse_tensor, tensor
3+
import mlir_finch.execution_engine
4+
import mlir_finch.passmanager
5+
from mlir_finch import ir
6+
from mlir_finch.dialects import arith, complex, func, linalg, sparse_tensor, tensor
77

88
from ._array import Array
99
from ._common import fn_cache
@@ -37,7 +37,7 @@ def get_add_module(
3737

3838
@func.FuncOp.from_py_func(a_tensor_type, b_tensor_type)
3939
def add(a, b):
40-
out = tensor.empty(out_tensor_type, [])
40+
out = tensor.empty(out_tensor_type.shape, dtype, encoding=out_tensor_type.encoding)
4141
generic_op = linalg.GenericOp(
4242
[out_tensor_type],
4343
[a, b],
@@ -72,7 +72,7 @@ def add(a, b):
7272
if DEBUG:
7373
(CWD / "add_module_opt.mlir").write_text(str(module))
7474

75-
return mlir.execution_engine.ExecutionEngine(module, opt_level=2, shared_libs=[MLIR_C_RUNNER_UTILS])
75+
return mlir_finch.execution_engine.ExecutionEngine(module, opt_level=2, shared_libs=[MLIR_C_RUNNER_UTILS])
7676

7777

7878
@fn_cache
@@ -97,7 +97,7 @@ def reshape(a, shape):
9797
if DEBUG:
9898
(CWD / "reshape_module_opt.mlir").write_text(str(module))
9999

100-
return mlir.execution_engine.ExecutionEngine(module, opt_level=2, shared_libs=[MLIR_C_RUNNER_UTILS])
100+
return mlir_finch.execution_engine.ExecutionEngine(module, opt_level=2, shared_libs=[MLIR_C_RUNNER_UTILS])
101101

102102

103103
@fn_cache
@@ -113,7 +113,9 @@ def get_broadcast_to_module(
113113

114114
@func.FuncOp.from_py_func(in_tensor_type)
115115
def broadcast_to(in_tensor):
116-
out = tensor.empty(out_tensor_type, [])
116+
out = tensor.empty(
117+
out_tensor_type.shape, out_tensor_type.element_type, encoding=out_tensor_type.encoding
118+
)
117119
return linalg.broadcast(in_tensor, outs=[out], dimensions=dimensions)
118120

119121
broadcast_to.func_op.attributes["llvm.emit_c_interface"] = ir.UnitAttr.get()
@@ -123,7 +125,7 @@ def broadcast_to(in_tensor):
123125
if DEBUG:
124126
(CWD / "broadcast_to_module_opt.mlir").write_text(str(module))
125127

126-
return mlir.execution_engine.ExecutionEngine(module, opt_level=2, shared_libs=[MLIR_C_RUNNER_UTILS])
128+
return mlir_finch.execution_engine.ExecutionEngine(module, opt_level=2, shared_libs=[MLIR_C_RUNNER_UTILS])
127129

128130

129131
def add(x1: Array, x2: Array) -> Array:

sparse/mlir_backend/levels.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
import re
66
import typing
77

8-
from mlir import ir
9-
from mlir.dialects import sparse_tensor
8+
from mlir_finch import ir
9+
from mlir_finch.dialects import sparse_tensor
1010

1111
import numpy as np
1212

@@ -36,6 +36,7 @@ def _camel_to_snake(name: str) -> str:
3636
class LevelProperties(enum.Flag):
3737
NonOrdered = enum.auto()
3838
NonUnique = enum.auto()
39+
SOA = enum.auto()
3940

4041
def build(self) -> list[sparse_tensor.LevelProperty]:
4142
return [getattr(sparse_tensor.LevelProperty, _camel_to_snake(p.name)) for p in type(self) if p in self]
@@ -108,15 +109,28 @@ def _get_ctypes_type(self, *, owns_memory=False):
108109
def get_fields():
109110
fields = []
110111
compressed_counter = 0
112+
singleton_counter = 0
111113
for level, next_level in itertools.zip_longest(self.levels, self.levels[1:]):
112114
if LevelFormat.Compressed == level.format:
113115
compressed_counter += 1
114116
fields.append((f"pointers_to_{compressed_counter}", get_nd_memref_descr(1, ptr_dtype)))
115117
if next_level is not None and LevelFormat.Singleton == next_level.format:
116-
fields.append((f"indices_{compressed_counter}", get_nd_memref_descr(2, idx_dtype)))
118+
singleton_counter += 1
119+
fields.append(
120+
(
121+
f"indices_{compressed_counter}_coords_{singleton_counter}",
122+
get_nd_memref_descr(1, idx_dtype),
123+
)
124+
)
117125
else:
118126
fields.append((f"indices_{compressed_counter}", get_nd_memref_descr(1, idx_dtype)))
119127

128+
if LevelFormat.Singleton == level.format:
129+
singleton_counter += 1
130+
fields.append(
131+
(f"indices_{compressed_counter}_coords_{singleton_counter}", get_nd_memref_descr(1, idx_dtype))
132+
)
133+
120134
fields.append(("values", get_nd_memref_descr(1, self.dtype)))
121135
return fields
122136

sparse/mlir_backend/tests/test_simple.py

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -176,20 +176,18 @@ def test_add(rng, dtype):
176176
assert isinstance(actual, np.ndarray)
177177
np.testing.assert_array_equal(actual, expected)
178178

179-
# NOTE: Fixed in https://github.com/llvm/llvm-project/pull/108615
180-
# actual = sparse.add(c_tensor, c_tensor).to_scipy_sparse()
181-
# expected = c + c
182-
# assert isinstance(actual, np.ndarray)
183-
# np.testing.assert_array_equal(actual, expected)
179+
actual = sparse.to_numpy(sparse.add(dense_tensor, dense_tensor))
180+
expected = dense + dense
181+
assert isinstance(actual, np.ndarray)
182+
np.testing.assert_array_equal(actual, expected)
184183

185184
actual = sparse.to_scipy(sparse.add(csr_2_tensor, coo_tensor))
186185
expected = csr_2 + coo
187186
assert_csx_equal(expected, actual)
188187

189-
# NOTE: https://discourse.llvm.org/t/passmanager-fails-on-simple-coo-addition-example/81247
190-
# actual = sparse.add(d_tensor, d_tensor).to_scipy_sparse()
191-
# expected = d + d
192-
# np.testing.assert_array_equal(actual.todense(), expected.todense())
188+
actual = sparse.to_scipy(sparse.add(coo_tensor, coo_tensor))
189+
expected = coo + coo
190+
np.testing.assert_array_equal(actual.todense(), expected.todense())
193191

194192

195193
@parametrize_dtypes
@@ -226,8 +224,11 @@ def test_coo_3d_format(dtype):
226224
format = sparse.levels.get_storage_format(
227225
levels=(
228226
sparse.levels.Level(sparse.levels.LevelFormat.Compressed, sparse.levels.LevelProperties.NonUnique),
229-
sparse.levels.Level(sparse.levels.LevelFormat.Singleton, sparse.levels.LevelProperties.NonUnique),
230-
sparse.levels.Level(sparse.levels.LevelFormat.Singleton, sparse.levels.LevelProperties(0)),
227+
sparse.levels.Level(
228+
sparse.levels.LevelFormat.Singleton,
229+
sparse.levels.LevelProperties.NonUnique | sparse.levels.LevelProperties.SOA,
230+
),
231+
sparse.levels.Level(sparse.levels.LevelFormat.Singleton, sparse.levels.LevelProperties.SOA),
231232
),
232233
order="C",
233234
pos_width=64,
@@ -237,20 +238,19 @@ def test_coo_3d_format(dtype):
237238

238239
SHAPE = (2, 2, 4)
239240
pos = np.array([0, 7])
240-
crd = np.array([[0, 1, 0, 0, 1, 1, 0], [1, 3, 1, 0, 0, 1, 0], [3, 1, 1, 0, 1, 1, 1]])
241+
crd = [np.array([0, 1, 0, 0, 1, 1, 0]), np.array([1, 3, 1, 0, 0, 1, 0]), np.array([3, 1, 1, 0, 1, 1, 1])]
241242
data = np.array([1, 2, 3, 4, 5, 6, 7], dtype=dtype)
242-
carrs = (pos, crd, data)
243+
carrs = (pos, *crd, data)
243244

244245
coo_array = sparse.from_constituent_arrays(format=format, arrays=carrs, shape=SHAPE)
245246
result = coo_array.get_constituent_arrays()
246247
for actual, expected in zip(result, carrs, strict=True):
247248
np.testing.assert_array_equal(actual, expected)
248249

249-
# NOTE: Blocked by https://github.com/llvm/llvm-project/pull/109135
250-
# res_arrays = sparse.add(coo_array, coo_array).get_constituent_arrays()
251-
# res_expected = (pos, crd, data * 2)
252-
# for actual, expected in zip(res_arrays, res_expected, strict=False):
253-
# np.testing.assert_array_equal(actual, expected)
250+
result_arrays = sparse.add(coo_array, coo_array).get_constituent_arrays()
251+
constituent_arrays = (pos, *crd, data * 2)
252+
for actual, expected in zip(result_arrays, constituent_arrays, strict=False):
253+
np.testing.assert_array_equal(actual, expected)
254254

255255

256256
@parametrize_dtypes

0 commit comments

Comments
 (0)