Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
P
pytensor
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
testgroup
pytensor
Commits
916da3f0
提交
916da3f0
authored
1月 16, 2026
作者:
Ricardo Vieira
提交者:
Ricardo Vieira
1月 19, 2026
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Numba sparse: Implement basic functionality
Co-authored-by:
Adrian Seyboldt
<
aseyboldt@users.noreply.github.com
>
Co-authored-by:
Jesse Grabowski
<
48652735+jessegrabowski@users.noreply.github.com
>
上级
6f23a126
隐藏空白字符变更
内嵌
并排
正在显示
6 个修改的文件
包含
420 行增加
和
213 行删除
+420
-213
basic.py
pytensor/link/numba/dispatch/basic.py
+16
-9
__init__.py
pytensor/link/numba/dispatch/sparse/__init__.py
+1
-0
variable.py
pytensor/link/numba/dispatch/sparse/variable.py
+139
-78
__init__.py
tests/link/numba/sparse/__init__.py
+0
-0
test_basic.py
tests/link/numba/sparse/test_basic.py
+264
-0
test_sparse.py
tests/link/numba/test_sparse.py
+0
-126
没有找到文件。
pytensor/link/numba/dispatch/basic.py
浏览文件 @
916da3f0
...
@@ -14,14 +14,13 @@ from pytensor.graph.basic import Apply, Constant, Variable
...
@@ -14,14 +14,13 @@ from pytensor.graph.basic import Apply, Constant, Variable
from
pytensor.graph.fg
import
FunctionGraph
from
pytensor.graph.fg
import
FunctionGraph
from
pytensor.graph.type
import
Type
from
pytensor.graph.type
import
Type
from
pytensor.link.numba.cache
import
compile_numba_function_src
,
hash_from_pickle_dump
from
pytensor.link.numba.cache
import
compile_numba_function_src
,
hash_from_pickle_dump
from
pytensor.link.numba.dispatch.sparse
import
CSCMatrixType
,
CSRMatrixType
from
pytensor.link.utils
import
(
from
pytensor.link.utils
import
(
fgraph_to_python
,
fgraph_to_python
,
)
)
from
pytensor.scalar.basic
import
ScalarType
from
pytensor.scalar.basic
import
ScalarType
from
pytensor.sparse
import
SparseTensorType
from
pytensor.sparse
import
SparseTensorType
from
pytensor.tensor.random.type
import
RandomGeneratorType
from
pytensor.tensor.random.type
import
RandomGeneratorType
from
pytensor.tensor.type
import
TensorType
from
pytensor.tensor.type
import
Dense
TensorType
from
pytensor.tensor.utils
import
hash_from_ndarray
from
pytensor.tensor.utils
import
hash_from_ndarray
from
pytensor.typed_list
import
TypedListType
from
pytensor.typed_list
import
TypedListType
...
@@ -112,7 +111,7 @@ def get_numba_type(
...
@@ -112,7 +111,7 @@ def get_numba_type(
Return Numba scalars for zero dimensional :class:`TensorType`\s.
Return Numba scalars for zero dimensional :class:`TensorType`\s.
"""
"""
if
isinstance
(
pytensor_type
,
TensorType
):
if
isinstance
(
pytensor_type
,
Dense
TensorType
):
dtype
=
pytensor_type
.
numpy_dtype
dtype
=
pytensor_type
.
numpy_dtype
numba_dtype
=
numba
.
from_dtype
(
dtype
)
numba_dtype
=
numba
.
from_dtype
(
dtype
)
if
force_scalar
or
(
if
force_scalar
or
(
...
@@ -125,18 +124,26 @@ def get_numba_type(
...
@@ -125,18 +124,26 @@ def get_numba_type(
numba_dtype
=
numba
.
from_dtype
(
dtype
)
numba_dtype
=
numba
.
from_dtype
(
dtype
)
return
numba_dtype
return
numba_dtype
elif
isinstance
(
pytensor_type
,
SparseTensorType
):
elif
isinstance
(
pytensor_type
,
SparseTensorType
):
dtype
=
pytensor_type
.
numpy_dtype
from
pytensor.link.numba.dispatch.sparse.variable
import
(
numba_dtype
=
numba
.
from_dtype
(
dtype
)
CSCMatrixType
,
CSRMatrixType
,
)
data_array
=
numba
.
types
.
Array
(
numba
.
from_dtype
(
pytensor_type
.
numpy_dtype
),
1
,
layout
)
indices_array
=
numba
.
types
.
Array
(
numba
.
from_dtype
(
np
.
int32
),
1
,
layout
)
indptr_array
=
numba
.
types
.
Array
(
numba
.
from_dtype
(
np
.
int32
),
1
,
layout
)
if
pytensor_type
.
format
==
"csr"
:
if
pytensor_type
.
format
==
"csr"
:
return
CSRMatrixType
(
numba_dtype
)
return
CSRMatrixType
(
data_array
,
indices_array
,
indptr_array
)
if
pytensor_type
.
format
==
"csc"
:
if
pytensor_type
.
format
==
"csc"
:
return
CSCMatrixType
(
numba_dtype
)
return
CSCMatrixType
(
data_array
,
indices_array
,
indptr_array
)
elif
isinstance
(
pytensor_type
,
RandomGeneratorType
):
elif
isinstance
(
pytensor_type
,
RandomGeneratorType
):
return
numba
.
types
.
NumPyRandomGeneratorType
(
"NumPyRandomGeneratorType"
)
return
numba
.
types
.
NumPyRandomGeneratorType
(
"NumPyRandomGeneratorType"
)
elif
isinstance
(
pytensor_type
,
TypedListType
):
elif
isinstance
(
pytensor_type
,
TypedListType
):
return
numba
.
types
.
List
(
get_numba_type
(
pytensor_type
.
ttype
))
return
numba
.
types
.
List
(
get_numba_type
(
pytensor_type
.
ttype
))
else
:
raise
NotImplementedError
(
f
"Numba type not implemented for {pytensor_type}"
)
raise
NotImplementedError
(
f
"Numba type not implemented for {pytensor_type}"
)
def
create_numba_signature
(
def
create_numba_signature
(
...
...
pytensor/link/numba/dispatch/sparse/__init__.py
0 → 100644
浏览文件 @
916da3f0
from
pytensor.link.numba.dispatch.sparse
import
variable
pytensor/link/numba/dispatch/sparse.py
→
pytensor/link/numba/dispatch/sparse
/variable
.py
浏览文件 @
916da3f0
import
numpy
as
np
import
numpy
as
np
import
scipy
as
sp
import
scipy
as
sp
import
scipy.sparse
from
numba.core
import
cgutils
,
types
from
numba.core
import
cgutils
,
types
from
numba.core.imputils
import
impl_ret_borrowed
from
numba.core.imputils
import
impl_ret_borrowed
,
lower_constant
from
numba.extending
import
(
from
numba.extending
import
(
NativeValue
,
NativeValue
,
box
,
box
,
...
@@ -27,17 +26,17 @@ class CSMatrixType(types.Type):
...
@@ -27,17 +26,17 @@ class CSMatrixType(types.Type):
def
instance_class
(
data
,
indices
,
indptr
,
shape
):
def
instance_class
(
data
,
indices
,
indptr
,
shape
):
raise
NotImplementedError
()
raise
NotImplementedError
()
def
__init__
(
self
,
dtype
):
def
__init__
(
self
,
d
ata_type
,
indices_type
,
indptr_
type
):
self
.
dtype
=
dtype
self
.
_key
=
(
data_type
,
indices_type
,
indptr_type
)
self
.
data
=
types
.
Array
(
dtype
,
1
,
"A"
)
self
.
data
=
data_type
self
.
indices
=
types
.
Array
(
types
.
int32
,
1
,
"A"
)
self
.
indices
=
indices_type
self
.
indptr
=
types
.
Array
(
types
.
int32
,
1
,
"A"
)
self
.
indptr
=
indptr_type
self
.
shape
=
types
.
UniTuple
(
types
.
int
64
,
2
)
self
.
shape
=
types
.
UniTuple
(
types
.
int
32
,
2
)
super
()
.
__init__
(
self
.
name
)
super
()
.
__init__
(
self
.
name
)
@property
@property
def
key
(
self
):
def
key
(
self
):
return
(
self
.
name
,
self
.
dtype
)
return
self
.
_key
make_attribute_wrapper
(
CSMatrixType
,
"data"
,
"data"
)
make_attribute_wrapper
(
CSMatrixType
,
"data"
,
"data"
)
...
@@ -63,31 +62,25 @@ class CSCMatrixType(CSMatrixType):
...
@@ -63,31 +62,25 @@ class CSCMatrixType(CSMatrixType):
@typeof_impl.register
(
sp
.
sparse
.
csc_matrix
)
@typeof_impl.register
(
sp
.
sparse
.
csc_matrix
)
def
typeof_csc_matrix
(
val
,
c
):
data
=
typeof_impl
(
val
.
data
,
c
)
return
CSCMatrixType
(
data
.
dtype
)
@typeof_impl.register
(
sp
.
sparse
.
csr_matrix
)
@typeof_impl.register
(
sp
.
sparse
.
csr_matrix
)
def
typeof_csr_matrix
(
val
,
c
):
def
typeof_cs_matrix
(
val
,
ctx
):
data
=
typeof_impl
(
val
.
data
,
c
)
match
val
:
return
CSRMatrixType
(
data
.
dtype
)
case
sp
.
sparse
.
csc_matrix
():
numba_type
=
CSCMatrixType
case
sp
.
sparse
.
csr_matrix
():
@register_model
(
CSRMatrixType
)
numba_type
=
CSRMatrixType
class
CSRMatrixModel
(
models
.
StructModel
):
case
_
:
def
__init__
(
self
,
dmm
,
fe_type
):
raise
ValueError
(
f
"val of type {type(val)} not recognized"
)
members
=
[
return
numba_type
(
(
"data"
,
fe_type
.
data
),
typeof_impl
(
val
.
data
,
ctx
),
(
"indices"
,
fe_type
.
indices
),
typeof_impl
(
val
.
indices
,
ctx
),
(
"indptr"
,
fe_type
.
indptr
),
typeof_impl
(
val
.
indptr
,
ctx
),
(
"shape"
,
fe_type
.
shape
),
)
]
super
()
.
__init__
(
dmm
,
fe_type
,
members
)
@register_model
(
CSCMatrixType
)
@register_model
(
CSCMatrixType
)
class
CSCMatrixModel
(
models
.
StructModel
):
@register_model
(
CSRMatrixType
)
class
CSMatrixModel
(
models
.
StructModel
):
def
__init__
(
self
,
dmm
,
fe_type
):
def
__init__
(
self
,
dmm
,
fe_type
):
members
=
[
members
=
[
(
"data"
,
fe_type
.
data
),
(
"data"
,
fe_type
.
data
),
...
@@ -98,21 +91,23 @@ class CSCMatrixModel(models.StructModel):
...
@@ -98,21 +91,23 @@ class CSCMatrixModel(models.StructModel):
super
()
.
__init__
(
dmm
,
fe_type
,
members
)
super
()
.
__init__
(
dmm
,
fe_type
,
members
)
@unbox
(
CSCMatrixType
)
@unbox
(
CSMatrixType
)
@unbox
(
CSRMatrixType
)
def
unbox_cs_matrix
(
typ
,
obj
,
c
):
def
unbox_matrix
(
typ
,
obj
,
c
):
struct_ptr
=
cgutils
.
create_struct_proxy
(
typ
)(
c
.
context
,
c
.
builder
)
struct_ptr
=
cgutils
.
create_struct_proxy
(
typ
)(
c
.
context
,
c
.
builder
)
# Get attributes from python object
data
=
c
.
pyapi
.
object_getattr_string
(
obj
,
"data"
)
data
=
c
.
pyapi
.
object_getattr_string
(
obj
,
"data"
)
indices
=
c
.
pyapi
.
object_getattr_string
(
obj
,
"indices"
)
indices
=
c
.
pyapi
.
object_getattr_string
(
obj
,
"indices"
)
indptr
=
c
.
pyapi
.
object_getattr_string
(
obj
,
"indptr"
)
indptr
=
c
.
pyapi
.
object_getattr_string
(
obj
,
"indptr"
)
shape
=
c
.
pyapi
.
object_getattr_string
(
obj
,
"shape"
)
shape
=
c
.
pyapi
.
object_getattr_string
(
obj
,
"shape"
)
# Unbox them into llvm struct
struct_ptr
.
data
=
c
.
unbox
(
typ
.
data
,
data
)
.
value
struct_ptr
.
data
=
c
.
unbox
(
typ
.
data
,
data
)
.
value
struct_ptr
.
indices
=
c
.
unbox
(
typ
.
indices
,
indices
)
.
value
struct_ptr
.
indices
=
c
.
unbox
(
typ
.
indices
,
indices
)
.
value
struct_ptr
.
indptr
=
c
.
unbox
(
typ
.
indptr
,
indptr
)
.
value
struct_ptr
.
indptr
=
c
.
unbox
(
typ
.
indptr
,
indptr
)
.
value
struct_ptr
.
shape
=
c
.
unbox
(
typ
.
shape
,
shape
)
.
value
struct_ptr
.
shape
=
c
.
unbox
(
typ
.
shape
,
shape
)
.
value
# Decref created attributes
c
.
pyapi
.
decref
(
data
)
c
.
pyapi
.
decref
(
data
)
c
.
pyapi
.
decref
(
indices
)
c
.
pyapi
.
decref
(
indices
)
c
.
pyapi
.
decref
(
indptr
)
c
.
pyapi
.
decref
(
indptr
)
...
@@ -120,15 +115,13 @@ def unbox_matrix(typ, obj, c):
...
@@ -120,15 +115,13 @@ def unbox_matrix(typ, obj, c):
is_error_ptr
=
cgutils
.
alloca_once_value
(
c
.
builder
,
cgutils
.
false_bit
)
is_error_ptr
=
cgutils
.
alloca_once_value
(
c
.
builder
,
cgutils
.
false_bit
)
is_error
=
c
.
builder
.
load
(
is_error_ptr
)
is_error
=
c
.
builder
.
load
(
is_error_ptr
)
res
=
NativeValue
(
struct_ptr
.
_getvalue
(),
is_error
=
is_error
)
res
=
NativeValue
(
struct_ptr
.
_getvalue
(),
is_error
=
is_error
)
return
res
return
res
@box
(
CSCMatrixType
)
@box
(
CSMatrixType
)
@box
(
CSRMatrixType
)
def
box_cs_matrix
(
typ
,
val
,
c
):
def
box_matrix
(
typ
,
val
,
c
):
struct_ptr
=
cgutils
.
create_struct_proxy
(
typ
)(
c
.
context
,
c
.
builder
,
value
=
val
)
struct_ptr
=
cgutils
.
create_struct_proxy
(
typ
)(
c
.
context
,
c
.
builder
,
value
=
val
)
data_obj
=
c
.
box
(
typ
.
data
,
struct_ptr
.
data
)
data_obj
=
c
.
box
(
typ
.
data
,
struct_ptr
.
data
)
...
@@ -136,11 +129,7 @@ def box_matrix(typ, val, c):
...
@@ -136,11 +129,7 @@ def box_matrix(typ, val, c):
indptr_obj
=
c
.
box
(
typ
.
indptr
,
struct_ptr
.
indptr
)
indptr_obj
=
c
.
box
(
typ
.
indptr
,
struct_ptr
.
indptr
)
shape_obj
=
c
.
box
(
typ
.
shape
,
struct_ptr
.
shape
)
shape_obj
=
c
.
box
(
typ
.
shape
,
struct_ptr
.
shape
)
c
.
pyapi
.
incref
(
data_obj
)
# Call scipy.sparse.cs[c|r]_matrix
c
.
pyapi
.
incref
(
indices_obj
)
c
.
pyapi
.
incref
(
indptr_obj
)
c
.
pyapi
.
incref
(
shape_obj
)
cls_obj
=
c
.
pyapi
.
unserialize
(
c
.
pyapi
.
serialize_object
(
typ
.
instance_class
))
cls_obj
=
c
.
pyapi
.
unserialize
(
c
.
pyapi
.
serialize_object
(
typ
.
instance_class
))
obj
=
c
.
pyapi
.
call_function_objargs
(
obj
=
c
.
pyapi
.
call_function_objargs
(
cls_obj
,
(
data_obj
,
indices_obj
,
indptr_obj
,
shape_obj
)
cls_obj
,
(
data_obj
,
indices_obj
,
indptr_obj
,
shape_obj
)
...
@@ -154,53 +143,125 @@ def box_matrix(typ, val, c):
...
@@ -154,53 +143,125 @@ def box_matrix(typ, val, c):
return
obj
return
obj
@overload
(
np
.
shape
)
def
_intrinsic_cs_codegen
(
context
,
builder
,
sig
,
args
):
def
overload_sparse_shape
(
x
):
matrix_type
=
sig
.
return_type
if
isinstance
(
x
,
CSMatrixType
):
struct
=
cgutils
.
create_struct_proxy
(
matrix_type
)(
context
,
builder
)
return
lambda
x
:
x
.
shape
data
,
indices
,
indptr
,
shape
=
args
struct
.
data
=
data
struct
.
indices
=
indices
struct
.
indptr
=
indptr
struct
.
shape
=
shape
return
impl_ret_borrowed
(
context
,
builder
,
matrix_type
,
struct
.
_getvalue
(),
)
@
overload_attribute
(
CSMatrixType
,
"ndim"
)
@
intrinsic
def
overload_sparse_ndim
(
inst
):
def
csr_matrix_from_components
(
typingctx
,
data
,
indices
,
indptr
,
shape
):
if
not
isinstance
(
inst
,
CSMatrixType
):
sig
=
CSRMatrixType
(
data
,
indices
,
indptr
)(
data
,
indices
,
indptr
,
shape
)
retur
n
return
sig
,
_intrinsic_cs_codege
n
def
ndim
(
inst
):
return
2
return
ndim
@intrinsic
def
csc_matrix_from_components
(
typingctx
,
data
,
indices
,
indptr
,
shape
):
sig
=
CSCMatrixType
(
data
,
indices
,
indptr
)(
data
,
indices
,
indptr
,
shape
)
return
sig
,
_intrinsic_cs_codegen
@intrinsic
@lower_constant
(
CSRMatrixType
)
def
_sparse_copy
(
typingctx
,
inst
,
data
,
indices
,
indptr
,
shape
):
@lower_constant
(
CSCMatrixType
)
def
_construct
(
context
,
builder
,
sig
,
args
):
def
cs_matrix_constant
(
context
,
builder
,
ty
,
pyval
):
typ
=
sig
.
return_type
data_const
=
context
.
make_constant_array
(
builder
,
ty
.
data
,
pyval
.
data
)
struct
=
cgutils
.
create_struct_proxy
(
typ
)(
context
,
builder
)
indices_const
=
context
.
make_constant_array
(
builder
,
ty
.
indices
,
pyval
.
indices
)
_
,
data
,
indices
,
indptr
,
shape
=
args
indptr_const
=
context
.
make_constant_array
(
builder
,
ty
.
indptr
,
pyval
.
indptr
)
struct
.
data
=
data
shape
=
context
.
get_constant_generic
(
builder
,
ty
.
shape
,
pyval
.
shape
)
struct
.
indices
=
indices
args
=
(
data_const
,
indices_const
,
indptr_const
,
shape
)
struct
.
indptr
=
indptr
struct
.
shape
=
shape
return
impl_ret_borrowed
(
context
,
builder
,
sig
.
return_type
,
struct
.
_getvalue
(),
)
sig
=
inst
(
inst
,
inst
.
data
,
inst
.
indices
,
inst
.
indptr
,
inst
.
shape
)
sig
=
ty
(
*
args
)
return
_intrinsic_cs_codegen
(
context
,
builder
,
sig
,
args
)
return
sig
,
_construct
@overload
(
sp
.
sparse
.
csr_matrix
)
def
overload_csr_matrix
(
arg1
,
shape
,
dtype
=
None
):
if
not
isinstance
(
arg1
,
types
.
BaseAnonymousTuple
)
or
len
(
arg1
)
!=
3
:
return
None
if
isinstance
(
shape
,
types
.
NoneType
):
return
None
def
impl
(
arg1
,
shape
,
dtype
=
None
):
data
,
indices
,
indptr
=
arg1
int32_shape
=
(
types
.
int32
(
shape
[
0
]),
types
.
int32
(
shape
[
1
]))
return
csr_matrix_from_components
(
data
,
indices
,
indptr
,
int32_shape
)
return
impl
@overload_method
(
CSMatrixType
,
"copy"
)
def
overload_sparse_copy
(
inst
):
if
not
isinstance
(
inst
,
CSMatrixType
):
return
def
copy
(
inst
):
@overload
(
sp
.
sparse
.
csc_matrix
)
return
_sparse_copy
(
def
overload_csc_matrix
(
arg1
,
shape
,
dtype
=
None
):
inst
,
inst
.
data
.
copy
(),
inst
.
indices
.
copy
(),
inst
.
indptr
.
copy
(),
inst
.
shape
if
not
isinstance
(
arg1
,
types
.
BaseAnonymousTuple
)
or
len
(
arg1
)
!=
3
:
return
None
if
isinstance
(
shape
,
types
.
NoneType
):
return
None
def
impl
(
arg1
,
shape
,
dtype
=
None
):
data
,
indices
,
indptr
=
arg1
int32_shape
=
(
types
.
int32
(
shape
[
0
]),
types
.
int32
(
shape
[
1
]))
return
csc_matrix_from_components
(
data
,
indices
,
indptr
,
int32_shape
)
return
impl
@overload
(
np
.
shape
)
def
overload_sparse_shape
(
matrix
):
if
isinstance
(
matrix
,
CSMatrixType
):
return
lambda
matrix
:
matrix
.
shape
@overload_attribute
(
CSMatrixType
,
"ndim"
)
def
overload_sparse_ndim
(
matrix
):
return
lambda
matrix
:
2
@overload_method
(
CSMatrixType
,
"copy"
)
def
overload_sparse_copy
(
matrix
):
match
matrix
:
case
CSRMatrixType
():
builder
=
csr_matrix_from_components
case
CSCMatrixType
():
builder
=
csc_matrix_from_components
case
_
:
return
def
copy
(
matrix
):
return
builder
(
matrix
.
data
.
copy
(),
matrix
.
indices
.
copy
(),
matrix
.
indptr
.
copy
(),
matrix
.
shape
,
)
)
return
copy
return
copy
@overload_method
(
CSMatrixType
,
"astype"
)
def
overload_sparse_astype
(
matrix
,
dtype
):
match
matrix
:
case
CSRMatrixType
():
builder
=
csr_matrix_from_components
case
CSCMatrixType
():
builder
=
csc_matrix_from_components
case
_
:
return
def
astype
(
matrix
,
dtype
):
return
builder
(
matrix
.
data
.
astype
(
dtype
),
matrix
.
indices
.
copy
(),
matrix
.
indptr
.
copy
(),
matrix
.
shape
,
)
return
astype
tests/link/numba/sparse/__init__.py
0 → 100644
浏览文件 @
916da3f0
tests/link/numba/sparse/test_basic.py
0 → 100644
浏览文件 @
916da3f0
from
functools
import
partial
from
sys
import
getrefcount
import
numpy
as
np
import
pytest
import
scipy
import
scipy
as
sp
import
pytensor.sparse
as
ps
import
pytensor.tensor
as
pt
from
pytensor.graph
import
Apply
,
Op
from
pytensor.sparse.variable
import
SparseConstant
from
pytensor.tensor.type
import
DenseTensorType
numba
=
pytest
.
importorskip
(
"numba"
)
# Make sure the Numba customizations are loaded
import
pytensor.link.numba.dispatch.sparse
# noqa: F401
from
pytensor
import
config
from
pytensor.sparse
import
SparseTensorType
from
tests.link.numba.test_basic
import
compare_numba_and_py
pytestmark
=
pytest
.
mark
.
filterwarnings
(
"error"
)
def
sparse_assert_fn
(
a
,
b
):
a_is_sparse
=
sp
.
sparse
.
issparse
(
a
)
assert
a_is_sparse
==
sp
.
sparse
.
issparse
(
b
)
if
a_is_sparse
:
assert
a
.
format
==
b
.
format
assert
a
.
dtype
==
b
.
dtype
assert
a
.
shape
==
b
.
shape
np
.
testing
.
assert_allclose
(
a
.
data
,
b
.
data
,
strict
=
True
)
np
.
testing
.
assert_allclose
(
a
.
indices
,
b
.
indices
,
strict
=
True
)
np
.
testing
.
assert_allclose
(
a
.
indptr
,
b
.
indptr
,
strict
=
True
)
else
:
np
.
testing
.
assert_allclose
(
a
,
b
,
strict
=
True
)
compare_numba_and_py_sparse
=
partial
(
compare_numba_and_py
,
assert_fn
=
sparse_assert_fn
)
def
test_sparse_boxing
():
@numba.njit
def
boxing_fn
(
x
,
y
):
return
x
,
y
,
y
.
data
.
sum
()
x_val
=
sp
.
sparse
.
csr_matrix
(
np
.
eye
(
100
))
y_val
=
sp
.
sparse
.
csc_matrix
(
np
.
eye
(
101
))
res_x_val
,
res_y_val
,
res_y_sum
=
boxing_fn
(
x_val
,
y_val
)
assert
np
.
array_equal
(
res_x_val
.
data
,
x_val
.
data
)
assert
np
.
array_equal
(
res_x_val
.
indices
,
x_val
.
indices
)
assert
np
.
array_equal
(
res_x_val
.
indptr
,
x_val
.
indptr
)
assert
res_x_val
.
shape
==
x_val
.
shape
assert
np
.
array_equal
(
res_y_val
.
data
,
y_val
.
data
)
assert
np
.
array_equal
(
res_y_val
.
indices
,
y_val
.
indices
)
assert
np
.
array_equal
(
res_y_val
.
indptr
,
y_val
.
indptr
)
assert
res_y_val
.
shape
==
y_val
.
shape
np
.
testing
.
assert_allclose
(
res_y_sum
,
y_val
.
sum
())
def
test_sparse_creation_refcount
():
@numba.njit
def
create_csr_matrix
(
data
,
indices
,
ind_ptr
):
return
scipy
.
sparse
.
csr_matrix
((
data
,
indices
,
ind_ptr
),
shape
=
(
5
,
5
))
x
=
scipy
.
sparse
.
random
(
5
,
5
,
density
=
0.5
,
format
=
"csr"
)
x_data
=
x
.
data
x_indptr
=
x
.
indptr
assert
getrefcount
(
x_data
)
==
3
assert
getrefcount
(
x_indptr
)
==
3
for
i
in
range
(
5
):
a
=
create_csr_matrix
(
x
.
data
,
x
.
indices
,
x
.
indptr
)
# a.data is a view of the underlying data under x.data, but doesn't reference it directly
assert
getrefcount
(
x_data
)
==
3
# x_indptr is reused directly
assert
getrefcount
(
x_indptr
)
==
4
del
a
assert
getrefcount
(
x_data
)
==
3
assert
getrefcount
(
x_indptr
)
==
3
def
test_sparse_passthrough_refcount
():
@numba.njit
def
identity
(
a
):
return
a
x
=
scipy
.
sparse
.
random
(
5
,
5
,
density
=
0.5
,
format
=
"csr"
)
x_data
=
x
.
data
assert
getrefcount
(
x_data
)
==
3
for
i
in
range
(
5
):
identity
(
x
)
assert
getrefcount
(
x_data
)
==
3
def
test_sparse_shape
():
@numba.njit
def
test_fn
(
x
):
return
np
.
shape
(
x
)
x_val
=
sp
.
sparse
.
csr_matrix
(
np
.
eye
(
100
))
res
=
test_fn
(
x_val
)
assert
res
==
(
100
,
100
)
def
test_sparse_ndim
():
@numba.njit
def
test_fn
(
x
):
return
x
.
ndim
x_val
=
sp
.
sparse
.
csr_matrix
(
np
.
eye
(
100
))
res
=
test_fn
(
x_val
)
assert
res
==
2
def
test_sparse_copy
():
@numba.njit
def
test_fn
(
x
):
return
x
.
copy
()
x
=
sp
.
sparse
.
csr_matrix
(
np
.
eye
(
100
))
y
=
test_fn
(
x
)
assert
y
is
not
x
for
attr
in
(
"data"
,
"indices"
,
"indptr"
):
y_data
=
getattr
(
y
,
attr
)
x_data
=
getattr
(
x
,
attr
)
assert
y_data
is
not
x_data
assert
not
np
.
shares_memory
(
y_data
,
x_data
)
assert
(
y_data
==
x_data
)
.
all
()
@pytest.mark.parametrize
(
"func"
,
[
sp
.
sparse
.
csr_matrix
,
sp
.
sparse
.
csc_matrix
],
ids
=
[
"csr"
,
"csc"
]
)
def
test_sparse_constructor
(
func
):
@numba.njit
def
csr_matrix_constructor
(
data
,
indices
,
indptr
):
return
func
((
data
,
indices
,
indptr
),
shape
=
(
3
,
3
))
inp
=
sp
.
sparse
.
random
(
3
,
3
,
density
=
0.5
,
format
=
"csr"
)
# Test with pure scipy constructor
out
=
func
((
inp
.
data
,
inp
.
indices
,
inp
.
indptr
),
copy
=
False
)
# Scipy does a useless slice on data and indices to trim away useless zeros
# which means these attributes are views of the original arrays.
assert
out
.
data
is
not
inp
.
data
assert
not
out
.
data
.
flags
.
owndata
assert
out
.
indices
is
not
inp
.
indices
assert
not
out
.
indices
.
flags
.
owndata
assert
out
.
indptr
is
inp
.
indptr
# Test numba impl
out_pt
=
csr_matrix_constructor
(
inp
.
data
,
inp
.
indices
,
inp
.
indptr
)
# Should work the same as Scipy's constructor, because it's ultimately used
assert
type
(
out_pt
)
is
type
(
out
)
assert
out_pt
.
data
is
not
inp
.
data
assert
not
out_pt
.
data
.
flags
.
owndata
assert
np
.
shares_memory
(
out_pt
.
data
,
inp
.
data
)
assert
(
out_pt
.
data
==
inp
.
data
)
.
all
()
assert
out_pt
.
indices
is
not
inp
.
indices
assert
not
out_pt
.
indices
.
flags
.
owndata
assert
np
.
shares_memory
(
out_pt
.
indices
,
inp
.
indices
)
assert
(
out_pt
.
indices
==
inp
.
indices
)
.
all
()
assert
out_pt
.
indptr
is
inp
.
indptr
@pytest.mark.parametrize
(
"cache"
,
[
True
,
False
])
@pytest.mark.parametrize
(
"format"
,
[
"csr"
,
"csc"
])
def
test_sparse_constant
(
format
,
cache
):
x
=
sp
.
sparse
.
random
(
3
,
3
,
density
=
0.5
,
format
=
format
,
random_state
=
166
)
x
=
ps
.
as_sparse
(
x
)
assert
isinstance
(
x
,
SparseConstant
)
assert
x
.
type
.
format
==
format
y
=
pt
.
vector
(
"y"
,
shape
=
(
3
,))
out
=
x
*
y
y_test
=
np
.
array
([
np
.
pi
,
np
.
e
,
np
.
euler_gamma
])
with
config
.
change_flags
(
numba__cache
=
cache
):
with
pytest
.
warns
(
UserWarning
,
match
=
r"Numba will use object mode to run SparseDenseVectorMultiply's perform method"
,
):
compare_numba_and_py_sparse
(
[
y
],
[
out
],
[
y_test
],
eval_obj_mode
=
False
,
)
@pytest.mark.parametrize
(
"format"
,
[
"csc"
,
"csr"
])
@pytest.mark.parametrize
(
"dense_out"
,
[
True
,
False
])
def
test_sparse_objmode
(
format
,
dense_out
):
class
SparseTestOp
(
Op
):
def
make_node
(
self
,
x
):
out
=
x
.
type
.
clone
(
shape
=
(
1
,
x
.
type
.
shape
[
-
1
]))()
if
dense_out
:
out
=
out
.
todense
()
.
type
()
return
Apply
(
self
,
[
x
],
[
out
])
def
perform
(
self
,
node
,
inputs
,
output_storage
):
[
x
]
=
inputs
[
out
]
=
output_storage
out
[
0
]
=
x
[
0
]
if
dense_out
:
out
[
0
]
=
out
[
0
]
.
todense
()
x
=
ps
.
matrix
(
format
,
dtype
=
config
.
floatX
,
shape
=
(
5
,
5
),
name
=
"x"
)
out
=
SparseTestOp
()(
x
)
assert
out
.
type
.
shape
==
(
1
,
5
)
assert
isinstance
(
out
.
type
,
DenseTensorType
if
dense_out
else
SparseTensorType
)
x_val
=
sp
.
sparse
.
random
(
5
,
5
,
density
=
0.25
,
dtype
=
config
.
floatX
,
format
=
format
)
with
pytest
.
warns
(
UserWarning
,
match
=
"Numba will use object mode to run SparseTestOp's perform method"
,
):
compare_numba_and_py_sparse
([
x
],
out
,
[
x_val
])
@pytest.mark.parametrize
(
"format"
,
[
"csr"
,
"csc"
])
def
test_simple_graph
(
format
):
x
=
ps
.
matrix
(
format
,
name
=
"x"
,
shape
=
(
3
,
3
))
y
=
pt
.
tensor
(
"y"
,
shape
=
(
3
,))
z
=
ps
.
math
.
sin
(
x
*
y
)
rng
=
np
.
random
.
default_rng
((
155
,
format
==
"csr"
))
x_test
=
sp
.
sparse
.
random
(
3
,
3
,
density
=
0.5
,
format
=
format
,
random_state
=
rng
)
y_test
=
rng
.
normal
(
size
=
(
3
,))
with
pytest
.
warns
(
UserWarning
,
match
=
r"Numba will use object mode to run .* perform method"
):
compare_numba_and_py_sparse
(
[
x
,
y
],
z
,
[
x_test
,
y_test
],
)
tests/link/numba/test_sparse.py
deleted
100644 → 0
浏览文件 @
6f23a126
from
functools
import
partial
import
numpy
as
np
import
pytest
import
scipy
as
sp
numba
=
pytest
.
importorskip
(
"numba"
)
# Make sure the Numba customizations are loaded
import
pytensor.link.numba.dispatch.sparse
# noqa: F401
from
pytensor
import
config
from
pytensor.sparse
import
Dot
,
SparseTensorType
from
tests.link.numba.test_basic
import
compare_numba_and_py
pytestmark
=
pytest
.
mark
.
filterwarnings
(
"error"
)
def
sparse_assert_fn
(
a
,
b
):
a_is_sparse
=
sp
.
sparse
.
issparse
(
a
)
assert
a_is_sparse
==
sp
.
sparse
.
issparse
(
b
)
if
a_is_sparse
:
assert
a
.
format
==
b
.
format
assert
a
.
dtype
==
b
.
dtype
assert
a
.
shape
==
b
.
shape
np
.
testing
.
assert_allclose
(
a
.
data
,
b
.
data
,
strict
=
True
)
np
.
testing
.
assert_allclose
(
a
.
indices
,
b
.
indices
,
strict
=
True
)
np
.
testing
.
assert_allclose
(
a
.
indptr
,
b
.
indptr
,
strict
=
True
)
else
:
np
.
testing
.
assert_allclose
(
a
,
b
,
strict
=
True
)
compare_numba_and_py_sparse
=
partial
(
compare_numba_and_py
,
assert_fn
=
sparse_assert_fn
)
def
test_sparse_unboxing
():
@numba.njit
def
test_unboxing
(
x
,
y
):
return
x
.
shape
,
y
.
shape
x_val
=
sp
.
sparse
.
csr_matrix
(
np
.
eye
(
100
))
y_val
=
sp
.
sparse
.
csc_matrix
(
np
.
eye
(
101
))
res
=
test_unboxing
(
x_val
,
y_val
)
assert
res
==
(
x_val
.
shape
,
y_val
.
shape
)
def
test_sparse_boxing
():
@numba.njit
def
test_boxing
(
x
,
y
):
return
x
,
y
x_val
=
sp
.
sparse
.
csr_matrix
(
np
.
eye
(
100
))
y_val
=
sp
.
sparse
.
csc_matrix
(
np
.
eye
(
101
))
res_x_val
,
res_y_val
=
test_boxing
(
x_val
,
y_val
)
assert
np
.
array_equal
(
res_x_val
.
data
,
x_val
.
data
)
assert
np
.
array_equal
(
res_x_val
.
indices
,
x_val
.
indices
)
assert
np
.
array_equal
(
res_x_val
.
indptr
,
x_val
.
indptr
)
assert
res_x_val
.
shape
==
x_val
.
shape
assert
np
.
array_equal
(
res_y_val
.
data
,
y_val
.
data
)
assert
np
.
array_equal
(
res_y_val
.
indices
,
y_val
.
indices
)
assert
np
.
array_equal
(
res_y_val
.
indptr
,
y_val
.
indptr
)
assert
res_y_val
.
shape
==
y_val
.
shape
def
test_sparse_shape
():
@numba.njit
def
test_fn
(
x
):
return
np
.
shape
(
x
)
x_val
=
sp
.
sparse
.
csr_matrix
(
np
.
eye
(
100
))
res
=
test_fn
(
x_val
)
assert
res
==
(
100
,
100
)
def
test_sparse_ndim
():
@numba.njit
def
test_fn
(
x
):
return
x
.
ndim
x_val
=
sp
.
sparse
.
csr_matrix
(
np
.
eye
(
100
))
res
=
test_fn
(
x_val
)
assert
res
==
2
def
test_sparse_copy
():
@numba.njit
def
test_fn
(
x
):
y
=
x
.
copy
()
return
(
y
is
not
x
and
np
.
all
(
x
.
data
==
y
.
data
)
and
np
.
all
(
x
.
indices
==
y
.
indices
)
)
x_val
=
sp
.
sparse
.
csr_matrix
(
np
.
eye
(
100
))
assert
test_fn
(
x_val
)
def
test_sparse_objmode
():
x
=
SparseTensorType
(
"csc"
,
dtype
=
config
.
floatX
)()
y
=
SparseTensorType
(
"csc"
,
dtype
=
config
.
floatX
)()
out
=
Dot
()(
x
,
y
)
x_val
=
sp
.
sparse
.
random
(
2
,
2
,
density
=
0.25
,
dtype
=
config
.
floatX
,
format
=
"csc"
)
y_val
=
sp
.
sparse
.
random
(
2
,
2
,
density
=
0.25
,
dtype
=
config
.
floatX
,
format
=
"csc"
)
with
pytest
.
warns
(
UserWarning
,
match
=
"Numba will use object mode to run SparseDot's perform method"
,
):
compare_numba_and_py_sparse
(
[
x
,
y
],
out
,
[
x_val
,
y_val
],
)
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论