Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
P
pytensor
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
testgroup
pytensor
Commits
88835278
提交
88835278
authored
11月 24, 2025
作者:
ricardoV94
提交者:
Ricardo Vieira
12月 15, 2025
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Tweak Blockwise/RandomVariable tests
上级
b3804f0e
显示空白字符变更
内嵌
并排
正在显示
10 个修改的文件
包含
96 行增加
和
32 行删除
+96
-32
blockwise.py
pytensor/tensor/blockwise.py
+5
-0
op.py
pytensor/tensor/random/op.py
+5
-0
numba.py
pytensor/tensor/random/rewriting/numba.py
+4
-2
test_rewriting.py
tests/tensor/linalg/test_rewriting.py
+3
-2
test_basic.py
tests/tensor/random/rewriting/test_basic.py
+6
-6
test_blockwise.py
tests/tensor/rewriting/test_blockwise.py
+4
-2
test_linalg.py
tests/tensor/rewriting/test_linalg.py
+6
-3
test_basic.py
tests/tensor/test_basic.py
+3
-2
test_blockwise.py
tests/tensor/test_blockwise.py
+55
-12
test_subtensor.py
tests/tensor/test_subtensor.py
+5
-3
没有找到文件。
pytensor/tensor/blockwise.py
浏览文件 @
88835278
...
...
@@ -594,6 +594,11 @@ class OpWithCoreShape(OpFromGraph):
class
BlockwiseWithCoreShape
(
OpWithCoreShape
):
"""Generalizes a Blockwise `Op` to include a core shape parameter."""
@property
def
core_op
(
self
):
[
blockwise_node
]
=
self
.
fgraph
.
apply_nodes
return
cast
(
Blockwise
,
blockwise_node
.
op
)
.
core_op
def
__str__
(
self
):
[
blockwise_node
]
=
self
.
fgraph
.
apply_nodes
return
f
"[{blockwise_node.op!s}]"
pytensor/tensor/random/op.py
浏览文件 @
88835278
...
...
@@ -497,6 +497,11 @@ def vectorize_random_variable(
class
RandomVariableWithCoreShape
(
OpWithCoreShape
):
"""Generalizes a random variable `Op` to include a core shape parameter."""
@property
def
core_op
(
self
):
[
rv_node
]
=
self
.
fgraph
.
apply_nodes
return
rv_node
.
op
def
__str__
(
self
):
[
rv_node
]
=
self
.
fgraph
.
apply_nodes
return
f
"[{rv_node.op!s}]"
pytensor/tensor/random/rewriting/numba.py
浏览文件 @
88835278
from
pytensor.compile
import
optdb
from
pytensor.graph
import
node_rewriter
from
pytensor.graph.rewriting.basic
import
dfs_rewriter
from
pytensor.graph.rewriting.basic
import
copy_stack_trace
,
dfs_rewriter
from
pytensor.tensor
import
as_tensor
,
constant
from
pytensor.tensor.random.op
import
RandomVariable
,
RandomVariableWithCoreShape
from
pytensor.tensor.rewriting.shape
import
ShapeFeature
...
...
@@ -69,7 +69,7 @@ def introduce_explicit_core_shape_rv(fgraph, node):
else
:
core_shape
=
as_tensor
(
core_shape
)
return
(
new_outs
=
(
RandomVariableWithCoreShape
(
[
core_shape
,
*
node
.
inputs
],
node
.
outputs
,
...
...
@@ -78,6 +78,8 @@ def introduce_explicit_core_shape_rv(fgraph, node):
.
make_node
(
core_shape
,
*
node
.
inputs
)
.
outputs
)
copy_stack_trace
(
node
.
outputs
,
new_outs
)
return
new_outs
optdb
.
register
(
...
...
tests/tensor/linalg/test_rewriting.py
浏览文件 @
88835278
...
...
@@ -13,7 +13,7 @@ from pytensor.tensor._linalg.solve.tridiagonal import (
LUFactorTridiagonal
,
SolveLUFactorTridiagonal
,
)
from
pytensor.tensor.blockwise
import
Blockwise
from
pytensor.tensor.blockwise
import
Blockwise
,
BlockwiseWithCoreShape
from
pytensor.tensor.linalg
import
solve
from
pytensor.tensor.slinalg
import
(
Cholesky
,
...
...
@@ -33,7 +33,8 @@ class DecompSolveOpCounter:
def
check_node_op_or_core_op
(
self
,
node
,
op
):
return
isinstance
(
node
.
op
,
op
)
or
(
isinstance
(
node
.
op
,
Blockwise
)
and
isinstance
(
node
.
op
.
core_op
,
op
)
isinstance
(
node
.
op
,
Blockwise
|
BlockwiseWithCoreShape
)
and
isinstance
(
node
.
op
.
core_op
,
op
)
)
def
count_vanilla_solve_nodes
(
self
,
nodes
)
->
int
:
...
...
tests/tensor/random/rewriting/test_basic.py
浏览文件 @
88835278
...
...
@@ -136,14 +136,14 @@ def test_inplace_rewrites(rv_op):
(
new_out
,
_new_rng
)
=
f
.
maker
.
fgraph
.
outputs
assert
new_out
.
type
==
out
.
type
new_node
=
new_out
.
owner
new_op
=
new_node
.
op
new_op
=
getattr
(
new_node
.
op
,
"core_op"
,
new_node
.
op
)
assert
isinstance
(
new_op
,
type
(
op
))
assert
new_op
.
_props_dict
()
==
(
op
.
_props_dict
()
|
{
"inplace"
:
True
})
assert
all
(
np
.
array_equal
(
a
.
data
,
b
.
data
)
for
a
,
b
in
zip
(
new_op
.
dist_params
(
new_node
),
op
.
dist_params
(
node
),
strict
=
True
)
)
assert
np
.
array_equal
(
new_op
.
size_param
(
new_node
)
.
data
,
op
.
size_param
(
node
)
.
data
)
#
assert all(
#
np.array_equal(a.data, b.data)
#
for a, b in zip(new_op.dist_params(new_node), op.dist_params(node), strict=True)
#
)
#
assert np.array_equal(new_op.size_param(new_node).data, op.size_param(node).data)
assert
check_stack_trace
(
f
)
...
...
tests/tensor/rewriting/test_blockwise.py
浏览文件 @
88835278
...
...
@@ -7,7 +7,7 @@ from pytensor.graph import FunctionGraph, rewrite_graph, vectorize_graph
from
pytensor.graph.basic
import
equal_computations
from
pytensor.scalar
import
log
as
scalar_log
from
pytensor.tensor
import
add
,
alloc
,
matrix
,
tensor
,
tensor3
from
pytensor.tensor.blockwise
import
Blockwise
from
pytensor.tensor.blockwise
import
Blockwise
,
BlockwiseWithCoreShape
from
pytensor.tensor.elemwise
import
Elemwise
from
pytensor.tensor.nlinalg
import
MatrixPinv
from
pytensor.tensor.rewriting.blockwise
import
local_useless_blockwise
...
...
@@ -40,7 +40,9 @@ def test_useless_unbatched_blockwise():
x
=
tensor3
(
"x"
)
out
=
blockwise_op
(
x
)
fn
=
function
([
x
],
out
,
mode
=
"FAST_COMPILE"
)
assert
isinstance
(
fn
.
maker
.
fgraph
.
outputs
[
0
]
.
owner
.
op
,
Blockwise
)
assert
isinstance
(
fn
.
maker
.
fgraph
.
outputs
[
0
]
.
owner
.
op
,
Blockwise
|
BlockwiseWithCoreShape
)
assert
isinstance
(
fn
.
maker
.
fgraph
.
outputs
[
0
]
.
owner
.
op
.
core_op
,
MatrixPinv
)
...
...
tests/tensor/rewriting/test_linalg.py
浏览文件 @
88835278
...
...
@@ -13,7 +13,7 @@ from pytensor.configdefaults import config
from
pytensor.graph
import
ancestors
from
pytensor.graph.rewriting.utils
import
rewrite_graph
from
pytensor.tensor
import
swapaxes
from
pytensor.tensor.blockwise
import
Blockwise
from
pytensor.tensor.blockwise
import
Blockwise
,
BlockwiseWithCoreShape
from
pytensor.tensor.elemwise
import
DimShuffle
from
pytensor.tensor.math
import
dot
,
matmul
from
pytensor.tensor.nlinalg
import
(
...
...
@@ -181,7 +181,10 @@ def test_cholesky_ldotlt(tag, cholesky_form, product, op):
no_cholesky_in_graph
=
not
any
(
isinstance
(
node
.
op
,
Cholesky
)
or
(
isinstance
(
node
.
op
,
Blockwise
)
and
isinstance
(
node
.
op
.
core_op
,
Cholesky
))
or
(
isinstance
(
node
.
op
,
Blockwise
|
BlockwiseWithCoreShape
)
and
isinstance
(
node
.
op
.
core_op
,
Cholesky
)
)
for
node
in
f
.
maker
.
fgraph
.
apply_nodes
)
...
...
@@ -287,7 +290,7 @@ class TestBatchedVectorBSolveToMatrixBSolve:
def
any_vector_b_solve
(
fn
):
return
any
(
(
isinstance
(
node
.
op
,
Blockwise
)
isinstance
(
node
.
op
,
Blockwise
|
BlockwiseWithCoreShape
)
and
isinstance
(
node
.
op
.
core_op
,
SolveBase
)
and
node
.
op
.
core_op
.
b_ndim
==
1
)
...
...
tests/tensor/test_basic.py
浏览文件 @
88835278
...
...
@@ -89,7 +89,7 @@ from pytensor.tensor.basic import (
where
,
zeros_like
,
)
from
pytensor.tensor.blockwise
import
Blockwise
from
pytensor.tensor.blockwise
import
Blockwise
,
BlockwiseWithCoreShape
from
pytensor.tensor.elemwise
import
DimShuffle
from
pytensor.tensor.exceptions
import
NotScalarConstantError
from
pytensor.tensor.math
import
dense_dot
...
...
@@ -4572,7 +4572,8 @@ def test_vectorize_join(axis, broadcasting_y):
blockwise_needed
=
isinstance
(
axis
,
SharedVariable
)
or
broadcasting_y
!=
"none"
has_blockwise
=
any
(
isinstance
(
node
.
op
,
Blockwise
)
for
node
in
vectorize_pt
.
maker
.
fgraph
.
apply_nodes
isinstance
(
node
.
op
,
Blockwise
|
BlockwiseWithCoreShape
)
for
node
in
vectorize_pt
.
maker
.
fgraph
.
apply_nodes
)
assert
has_blockwise
==
blockwise_needed
...
...
tests/tensor/test_blockwise.py
浏览文件 @
88835278
...
...
@@ -12,6 +12,7 @@ from pytensor.compile.function.types import add_supervisor_to_fgraph
from
pytensor.gradient
import
grad
from
pytensor.graph
import
Apply
,
FunctionGraph
,
Op
,
rewrite_graph
from
pytensor.graph.replace
import
vectorize_graph
,
vectorize_node
from
pytensor.link.numba
import
NumbaLinker
from
pytensor.raise_op
import
assert_op
from
pytensor.tensor
import
(
diagonal
,
...
...
@@ -23,7 +24,11 @@ from pytensor.tensor import (
tensor
,
vector
,
)
from
pytensor.tensor.blockwise
import
Blockwise
,
vectorize_node_fallback
from
pytensor.tensor.blockwise
import
(
Blockwise
,
BlockwiseWithCoreShape
,
vectorize_node_fallback
,
)
from
pytensor.tensor.nlinalg
import
MatrixInverse
,
eig
from
pytensor.tensor.rewriting.blas
import
specialize_matmul_to_batched_dot
from
pytensor.tensor.signal
import
convolve1d
...
...
@@ -39,6 +44,11 @@ from pytensor.tensor.slinalg import (
from
pytensor.tensor.utils
import
_parse_gufunc_signature
@pytest.mark.xfail
(
condition
=
isinstance
(
get_default_mode
()
.
linker
,
NumbaLinker
),
raises
=
TypeError
,
reason
=
"Numba scalar blockwise obj-mode fallback fails: https://github.com/pymc-devs/pytensor/issues/1760"
,
)
def
test_perform_method_per_node
():
"""Confirm that Blockwise uses one perform method per node.
...
...
@@ -66,8 +76,13 @@ def test_perform_method_per_node():
fn
=
pytensor
.
function
([
x
,
y
],
[
out_x
,
out_y
])
[
op1
,
op2
]
=
[
node
.
op
for
node
in
fn
.
maker
.
fgraph
.
apply_nodes
]
# Confirm both nodes have the same Op
assert
op1
is
blockwise_op
assert
op1
is
op2
assert
isinstance
(
op1
,
Blockwise
|
BlockwiseWithCoreShape
)
and
isinstance
(
op1
.
core_op
,
NodeDependentPerformOp
)
assert
isinstance
(
op2
,
Blockwise
|
BlockwiseWithCoreShape
)
and
isinstance
(
op2
.
core_op
,
NodeDependentPerformOp
)
# assert op1 is op2 # Not true in the Numba backend
res_out_x
,
res_out_y
=
fn
(
np
.
zeros
(
3
,
dtype
=
"float32"
),
np
.
zeros
(
3
,
dtype
=
"int32"
))
np
.
testing
.
assert_array_equal
(
res_out_x
,
np
.
ones
(
3
,
dtype
=
"float32"
))
...
...
@@ -120,7 +135,9 @@ def check_blockwise_runtime_broadcasting(mode):
out
,
mode
=
get_mode
(
mode
)
.
excluding
(
specialize_matmul_to_batched_dot
.
__name__
),
)
assert
isinstance
(
fn
.
maker
.
fgraph
.
outputs
[
0
]
.
owner
.
op
,
Blockwise
)
assert
isinstance
(
fn
.
maker
.
fgraph
.
outputs
[
0
]
.
owner
.
op
,
Blockwise
|
BlockwiseWithCoreShape
)
for
valid_test_values
in
[
(
...
...
@@ -292,8 +309,8 @@ def test_blockwise_infer_core_shape():
def
perform
(
self
,
node
,
inputs
,
outputs
):
a
,
b
=
inputs
c
,
d
=
outputs
c
[
0
]
=
np
.
arange
(
a
.
size
+
b
.
size
)
d
[
0
]
=
np
.
arange
(
a
.
sum
()
+
b
.
sum
())
c
[
0
]
=
np
.
arange
(
a
.
size
+
b
.
size
,
dtype
=
config
.
floatX
)
d
[
0
]
=
np
.
arange
(
a
.
sum
()
+
b
.
sum
()
,
dtype
=
config
.
floatX
)
def
infer_shape
(
self
,
fgraph
,
node
,
input_shapes
):
# First output shape depends only on input_shapes
...
...
@@ -389,7 +406,13 @@ class BlockwiseOpTester:
tensor
(
shape
=
(
None
,)
*
len
(
param_sig
))
for
param_sig
in
self
.
params_sig
]
core_func
=
pytensor
.
function
(
base_inputs
,
self
.
core_op
(
*
base_inputs
))
np_func
=
np
.
vectorize
(
core_func
,
signature
=
self
.
signature
)
def
inp_copy_core_func
(
*
args
):
# Work-around for https://github.com/numba/numba/issues/10357
# numpy vectorize passes non-writeable arrays to the inner function
return
core_func
(
*
(
arg
.
copy
()
for
arg
in
args
))
np_func
=
np
.
vectorize
(
inp_copy_core_func
,
signature
=
self
.
signature
)
for
vec_inputs
,
vec_inputs_testvals
in
self
.
create_batched_inputs
():
pt_func
=
pytensor
.
function
(
vec_inputs
,
self
.
block_op
(
*
vec_inputs
))
...
...
@@ -408,13 +431,26 @@ class BlockwiseOpTester:
]
out
=
self
.
core_op
(
*
base_inputs
)
.
sum
()
# Create separate numpy vectorized functions for each input
def
copy_inputs_wrapper
(
fn
):
# Work-around for https://github.com/numba/numba/issues/10357
# numpy vectorize passes non-writeable arrays to the inner function
def
copy_fn
(
*
args
):
return
fn
(
*
(
arg
.
copy
()
for
arg
in
args
))
return
copy_fn
np_funcs
=
[]
for
i
,
inp
in
enumerate
(
base_inputs
):
core_grad_func
=
pytensor
.
function
(
base_inputs
,
grad
(
out
,
wrt
=
inp
))
params_sig
=
self
.
signature
.
split
(
"->"
)[
0
]
param_sig
=
f
"({','.join(self.params_sig[i])})"
grad_sig
=
f
"{params_sig}->{param_sig}"
np_func
=
np
.
vectorize
(
core_grad_func
,
signature
=
grad_sig
)
np_func
=
np
.
vectorize
(
copy_inputs_wrapper
(
core_grad_func
),
signature
=
grad_sig
,
)
np_funcs
.
append
(
np_func
)
# We test gradient wrt to one batched input at a time
...
...
@@ -506,7 +542,9 @@ def test_small_blockwise_performance(benchmark):
b
=
dmatrix
(
shape
=
(
7
,
20
))
out
=
convolve1d
(
a
,
b
,
mode
=
"valid"
)
fn
=
pytensor
.
function
([
a
,
b
],
out
,
trust_input
=
True
)
assert
isinstance
(
fn
.
maker
.
fgraph
.
outputs
[
0
]
.
owner
.
op
,
Blockwise
)
assert
isinstance
(
fn
.
maker
.
fgraph
.
outputs
[
0
]
.
owner
.
op
,
Blockwise
|
BlockwiseWithCoreShape
)
rng
=
np
.
random
.
default_rng
(
495
)
a_test
=
rng
.
normal
(
size
=
a
.
type
.
shape
)
...
...
@@ -529,7 +567,10 @@ def test_cop_with_params():
fn
=
pytensor
.
function
([
x
],
x_shape
)
[
fn_out
]
=
fn
.
maker
.
fgraph
.
outputs
assert
fn_out
.
owner
.
op
==
matrix_assert
,
"Blockwise should be in final graph"
op
=
fn_out
.
owner
.
op
assert
(
isinstance
(
op
,
Blockwise
|
BlockwiseWithCoreShape
)
and
op
.
core_op
==
assert_op
),
"Blockwise should be in final graph"
np
.
testing
.
assert_allclose
(
fn
(
np
.
zeros
((
5
,
3
,
2
))),
...
...
@@ -557,7 +598,7 @@ class TestInplace:
[
cholesky_op
]
=
[
node
.
op
.
core_op
for
node
in
f
.
maker
.
fgraph
.
apply_nodes
if
isinstance
(
node
.
op
,
Blockwise
)
if
isinstance
(
node
.
op
,
Blockwise
|
BlockwiseWithCoreShape
)
and
isinstance
(
node
.
op
.
core_op
,
Cholesky
)
]
else
:
...
...
@@ -603,7 +644,9 @@ class TestInplace:
op
=
fn
.
maker
.
fgraph
.
outputs
[
0
]
.
owner
.
op
if
batched_A
or
batched_b
:
assert
isinstance
(
op
,
Blockwise
)
and
isinstance
(
op
.
core_op
,
SolveBase
)
assert
isinstance
(
op
,
Blockwise
|
BlockwiseWithCoreShape
)
and
isinstance
(
op
.
core_op
,
SolveBase
)
if
batched_A
and
not
batched_b
:
if
solve_fn
==
solve
:
assert
op
.
destroy_map
==
{
0
:
[
0
]}
...
...
tests/tensor/test_subtensor.py
浏览文件 @
88835278
...
...
@@ -25,7 +25,7 @@ from pytensor.link.numba import NumbaLinker
from
pytensor.printing
import
pprint
from
pytensor.scalar.basic
import
as_scalar
,
int16
from
pytensor.tensor
import
as_tensor
,
constant
,
get_vector_length
,
vectorize
from
pytensor.tensor.blockwise
import
Blockwise
from
pytensor.tensor.blockwise
import
Blockwise
,
BlockwiseWithCoreShape
from
pytensor.tensor.elemwise
import
DimShuffle
from
pytensor.tensor.math
import
exp
,
isinf
,
lt
,
switch
from
pytensor.tensor.math
import
sum
as
pt_sum
...
...
@@ -3034,7 +3034,8 @@ def test_vectorize_subtensor_without_batch_indices():
[
x
,
start
],
vectorize
(
core_fn
,
signature
=
signature
)(
x
,
start
)
)
assert
any
(
isinstance
(
node
.
op
,
Blockwise
)
for
node
in
vectorize_pt
.
maker
.
fgraph
.
apply_nodes
isinstance
(
node
.
op
,
Blockwise
|
BlockwiseWithCoreShape
)
for
node
in
vectorize_pt
.
maker
.
fgraph
.
apply_nodes
)
x_test
=
np
.
random
.
normal
(
size
=
x
.
type
.
shape
)
.
astype
(
x
.
type
.
dtype
)
start_test
=
np
.
random
.
randint
(
0
,
x
.
type
.
shape
[
-
2
],
size
=
start
.
type
.
shape
[
0
])
...
...
@@ -3116,7 +3117,8 @@ def test_vectorize_adv_subtensor(
)
has_blockwise
=
any
(
isinstance
(
node
.
op
,
Blockwise
)
for
node
in
vectorize_pt
.
maker
.
fgraph
.
apply_nodes
isinstance
(
node
.
op
,
Blockwise
|
BlockwiseWithCoreShape
)
for
node
in
vectorize_pt
.
maker
.
fgraph
.
apply_nodes
)
assert
has_blockwise
==
uses_blockwise
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论