Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
P
pytensor
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
testgroup
pytensor
Commits
90dd93d0
提交
90dd93d0
authored
12月 21, 2016
作者:
Frédéric Bastien
提交者:
GitHub
12月 21, 2016
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #5317 from khaotik/cumop
Merge CumsumOp/CumprodOp into CumOp
上级
170aff07
1455b49c
隐藏空白字符变更
内嵌
并排
正在显示
6 个修改的文件
包含
296 行增加
和
346 行删除
+296
-346
extra_ops.py
theano/gpuarray/extra_ops.py
+106
-84
test_extra_ops.py
theano/gpuarray/tests/test_extra_ops.py
+99
-74
extra_ops.py
theano/sandbox/cuda/extra_ops.py
+7
-4
test_extra_ops.py
theano/sandbox/cuda/tests/test_extra_ops.py
+3
-3
extra_ops.py
theano/tensor/extra_ops.py
+61
-123
test_extra_ops.py
theano/tensor/tests/test_extra_ops.py
+20
-58
没有找到文件。
theano/gpuarray/extra_ops.py
浏览文件 @
90dd93d0
from
__future__
import
absolute_import
,
print_function
,
division
from
__future__
import
absolute_import
,
print_function
,
division
import
os
import
os
from
theano
import
Apply
,
Op
from
theano
import
Apply
,
Op
from
theano.tensor.extra_ops
import
Cum
sum
Op
from
theano.tensor.extra_ops
import
CumOp
from
.basic_ops
import
infer_context_name
from
.basic_ops
import
infer_context_name
try
:
try
:
from
pygpu
import
gpuarray
from
pygpu
import
gpuarray
...
@@ -12,7 +12,7 @@ from .basic_ops import (as_gpuarray_variable, GpuKernelBase, Kernel, GpuReshape)
...
@@ -12,7 +12,7 @@ from .basic_ops import (as_gpuarray_variable, GpuKernelBase, Kernel, GpuReshape)
from
.opt
import
register_opt
,
op_lifter
,
register_opt2
from
.opt
import
register_opt
,
op_lifter
,
register_opt2
class
GpuCum
sum
(
GpuKernelBase
,
Op
):
class
GpuCum
Op
(
GpuKernelBase
,
Op
):
"""
"""
Parameters
Parameters
----------
----------
...
@@ -20,10 +20,19 @@ class GpuCumsum(GpuKernelBase, Op):
...
@@ -20,10 +20,19 @@ class GpuCumsum(GpuKernelBase, Op):
Can not be None. If you want the array flattened, do it before.
Can not be None. If you want the array flattened, do it before.
"""
"""
SUPPORTED_NDIMS
=
3
SUPPORTED_NDIMS
=
3
__props__
=
(
'axis'
,)
__props__
=
(
'axis'
,
'mode'
)
def
__init__
(
self
,
axis
):
def
__init__
(
self
,
axis
,
mode
=
'add'
):
self
.
axis
=
axis
self
.
axis
=
axis
if
axis
else
0
self
.
mode
=
mode
def
__eq__
(
self
,
other
):
if
type
(
other
)
!=
type
(
self
):
return
False
return
self
.
axis
==
other
.
axis
and
self
.
mode
==
other
.
mode
def
__hash__
(
self
):
return
hash
(
self
.
axis
)
^
hash
(
self
.
mode
)
def
c_code_cache_version
(
self
):
def
c_code_cache_version
(
self
):
return
(
3
,)
return
(
3
,)
...
@@ -38,14 +47,14 @@ class GpuCumsum(GpuKernelBase, Op):
...
@@ -38,14 +47,14 @@ class GpuCumsum(GpuKernelBase, Op):
return
node
.
inputs
[
0
]
.
type
.
context
return
node
.
inputs
[
0
]
.
type
.
context
def
make_node
(
self
,
x
):
def
make_node
(
self
,
x
):
assert
x
.
type
.
dtype
==
'float32'
,
"Only float32 supported for GpuCum
Sum
"
assert
x
.
type
.
dtype
==
'float32'
,
"Only float32 supported for GpuCum
Op
"
context_name
=
infer_context_name
(
x
)
context_name
=
infer_context_name
(
x
)
x
=
as_gpuarray_variable
(
x
,
context_name
)
x
=
as_gpuarray_variable
(
x
,
context_name
)
if
x
.
ndim
>
GpuCum
sum
.
SUPPORTED_NDIMS
:
if
x
.
ndim
>
GpuCum
Op
.
SUPPORTED_NDIMS
:
raise
NotImplementedError
(
'Only cum
sum
on 1D, 2D and
\
raise
NotImplementedError
(
'Only cum
op
on 1D, 2D and
\
3D arrays are supported right now!'
)
3D arrays are supported right now!'
)
if
self
.
axis
>=
x
.
ndim
or
self
.
axis
<
-
x
.
ndim
:
if
self
.
axis
>=
x
.
ndim
or
self
.
axis
<
-
x
.
ndim
:
...
@@ -56,6 +65,7 @@ class GpuCumsum(GpuKernelBase, Op):
...
@@ -56,6 +65,7 @@ class GpuCumsum(GpuKernelBase, Op):
kernels
=
[]
kernels
=
[]
# cumadd
# cumadd
kname
=
"k_cumadd"
kname
=
"k_cumadd"
op
=
{
'mul'
:
'*'
,
'add'
:
'+'
}[
self
.
mode
]
k_var
=
"k_cumadd_"
+
nodename
k_var
=
"k_cumadd_"
+
nodename
dtype_x
=
node
.
inputs
[
0
]
.
dtype
dtype_x
=
node
.
inputs
[
0
]
.
dtype
flags
=
Kernel
.
get_flags
(
dtype_x
)
flags
=
Kernel
.
get_flags
(
dtype_x
)
...
@@ -75,7 +85,7 @@ class GpuCumsum(GpuKernelBase, Op):
...
@@ -75,7 +85,7 @@ class GpuCumsum(GpuKernelBase, Op):
int idx_last_input = lastElementIdx*inputStrides_x + dataOffsetY_input;
int idx_last_input = lastElementIdx*inputStrides_x + dataOffsetY_input;
int idx_last_output = lastElementIdx*outputStrides_x + dataOffsetY_output;
int idx_last_output = lastElementIdx*outputStrides_x + dataOffsetY_output;
int idx_beforelast = beforeLastElementIdx*outputStrides_x + dataOffsetY_output;
int idx_beforelast = beforeLastElementIdx*outputStrides_x + dataOffsetY_output;
output[idx_last_output] = input[idx_last_input]
+
output[idx_beforelast];
output[idx_last_output] = input[idx_last_input]
%(op)
s
output[idx_beforelast];
}
}
"""
%
locals
()
"""
%
locals
()
params
=
[
gpuarray
.
GpuArray
,
gpuarray
.
GpuArray
,
gpuarray
.
SSIZE
,
params
=
[
gpuarray
.
GpuArray
,
gpuarray
.
GpuArray
,
gpuarray
.
SSIZE
,
...
@@ -86,9 +96,9 @@ class GpuCumsum(GpuKernelBase, Op):
...
@@ -86,9 +96,9 @@ class GpuCumsum(GpuKernelBase, Op):
]
]
kernels
.
append
(
Kernel
(
code
=
code
,
name
=
kname
,
params
=
params
,
kernels
.
append
(
Kernel
(
code
=
code
,
name
=
kname
,
params
=
params
,
flags
=
flags
,
objvar
=
k_var
))
flags
=
flags
,
objvar
=
k_var
))
# blockCum
Sum
# blockCum
Op
kname
=
"k_blockCum
Sum
"
kname
=
"k_blockCum
Op
"
k_var
=
"k_blockCum
Sum
_"
+
nodename
k_var
=
"k_blockCum
Op
_"
+
nodename
params
=
[
gpuarray
.
GpuArray
,
gpuarray
.
GpuArray
,
gpuarray
.
SIZE
,
params
=
[
gpuarray
.
GpuArray
,
gpuarray
.
GpuArray
,
gpuarray
.
SIZE
,
gpuarray
.
SSIZE
,
gpuarray
.
SSIZE
,
gpuarray
.
SSIZE
,
gpuarray
.
SSIZE
,
gpuarray
.
SSIZE
,
gpuarray
.
SSIZE
,
gpuarray
.
SSIZE
,
gpuarray
.
SSIZE
,
gpuarray
.
SSIZE
,
gpuarray
.
SSIZE
,
gpuarray
.
SSIZE
,
gpuarray
.
SSIZE
,
...
@@ -96,109 +106,108 @@ class GpuCumsum(GpuKernelBase, Op):
...
@@ -96,109 +106,108 @@ class GpuCumsum(GpuKernelBase, Op):
code
=
"""
code
=
"""
// helper functions
// helper functions
WITHIN_KERNEL
WITHIN_KERNEL
void k_reductionPhase(float* partialCum
Sum
) {
void k_reductionPhase(float* partialCum
Op
) {
// Traverse down from leaves to root building partial sums at internal nodes in the tree.
// Traverse down from leaves to root building partial sums at internal nodes in the tree.
for (unsigned int stride = 1; stride <= blockDim.x; stride *= 2) {
for (unsigned int stride = 1; stride <= blockDim.x; stride *= 2) {
local_barrier();
local_barrier();
unsigned int index = (threadIdx.x + 1) * (stride * 2) - 1;
unsigned int index = (threadIdx.x + 1) * (stride * 2) - 1;
if(index < blockDim.x*2) {
if(index < blockDim.x*2) {
partialCum
Sum[index] += partialCumSum
[index - stride];
partialCum
Op[index]
%(op)
s= partialCumOp
[index - stride];
}
}
}
}
}
}
WITHIN_KERNEL
WITHIN_KERNEL
void k_fetchData(float* partialCum
Sum
, float* input, int globalThreadID,
void k_fetchData(float* partialCum
Op
, float* input, int globalThreadID,
ga_ssize dataStrides_x, ga_ssize dataStrides_y, ga_ssize dataStrides_z,
ga_ssize dataStrides_x, ga_ssize dataStrides_y, ga_ssize dataStrides_z,
int offsetY, int offsetZ) {
int offsetY, int offsetZ) {
// blockIdx.y and blockIdx.z represents the current independent cum
sum
// blockIdx.y and blockIdx.z represents the current independent cum
op
int idY = blockIdx.y + offsetY;
int idY = blockIdx.y + offsetY;
int idZ = blockIdx.z + offsetZ; int offset = idY * dataStrides_y + idZ * dataStrides_z;
int idZ = blockIdx.z + offsetZ; int offset = idY * dataStrides_y + idZ * dataStrides_z;
int idx_even = (globalThreadID*2 ) * dataStrides_x + offset;
int idx_even = (globalThreadID*2 ) * dataStrides_x + offset;
int idx_odd = (globalThreadID*2 + 1) * dataStrides_x + offset;
int idx_odd = (globalThreadID*2 + 1) * dataStrides_x + offset;
partialCum
Sum
[threadIdx.x*2] = input[idx_even];
partialCum
Op
[threadIdx.x*2] = input[idx_even];
partialCum
Sum
[threadIdx.x*2 + 1] = input[idx_odd];
partialCum
Op
[threadIdx.x*2 + 1] = input[idx_odd];
}
}
WITHIN_KERNEL
WITHIN_KERNEL
void k_reversePhase(float* partialCum
Sum
) {
void k_reversePhase(float* partialCum
Op
) {
// Traverse back up the tree building the scan from the partial sums
// Traverse back up the tree building the scan from the partial sums
for (unsigned int stride = exp2(ceil(log2((float)blockDim.x))); stride > 0; stride /= 2) {
for (unsigned int stride = exp2(ceil(log2((float)blockDim.x))); stride > 0; stride /= 2) {
local_barrier();
local_barrier();
unsigned int index = (threadIdx.x + 1) * (stride * 2) - 1;
unsigned int index = (threadIdx.x + 1) * (stride * 2) - 1;
if(index + stride < blockDim.x*2) {
if(index + stride < blockDim.x*2) {
partialCum
Sum[index + stride] += partialCumSum
[index];
partialCum
Op[index + stride]
%(op)
s= partialCumOp
[index];
}
}
}
}
}
}
WITHIN_KERNEL
WITHIN_KERNEL
void k_pushData(float* partialCum
Sum
, float* output, int globalThreadID,
void k_pushData(float* partialCum
Op
, float* output, int globalThreadID,
ga_ssize dataStrides_x, ga_ssize dataStrides_y, ga_ssize dataStrides_z,
ga_ssize dataStrides_x, ga_ssize dataStrides_y, ga_ssize dataStrides_z,
int offsetY, int offsetZ) {
int offsetY, int offsetZ) {
local_barrier();
local_barrier();
// blockIdx.y and blockIdx.z represents the current independent cum
sum
// blockIdx.y and blockIdx.z represents the current independent cum
op
int idY = blockIdx.y + offsetY;
int idY = blockIdx.y + offsetY;
int idZ = blockIdx.z + offsetZ;
int idZ = blockIdx.z + offsetZ;
int offset = idY * dataStrides_y + idZ * dataStrides_z;
int offset = idY * dataStrides_y + idZ * dataStrides_z;
int idx_even = (globalThreadID*2 ) * dataStrides_x + offset;
int idx_even = (globalThreadID*2 ) * dataStrides_x + offset;
int idx_odd = (globalThreadID*2 + 1) * dataStrides_x + offset;
int idx_odd = (globalThreadID*2 + 1) * dataStrides_x + offset;
output[idx_even] = partialCum
Sum
[threadIdx.x*2];
output[idx_even] = partialCum
Op
[threadIdx.x*2];
output[idx_odd] = partialCum
Sum
[threadIdx.x*2 + 1];
output[idx_odd] = partialCum
Op
[threadIdx.x*2 + 1];
}
}
KERNEL void k_blockCum
Sum
(float* input, float* output,
KERNEL void k_blockCum
Op
(float* input, float* output,
size_t nbElementsPerCum
sum
, ga_ssize inputStrides_x,
size_t nbElementsPerCum
Op
, ga_ssize inputStrides_x,
ga_ssize inputStrides_y, ga_ssize inputStrides_z,
ga_ssize inputStrides_y, ga_ssize inputStrides_z,
ga_ssize outputStrides_x, ga_ssize outputStrides_y,
ga_ssize outputStrides_x, ga_ssize outputStrides_y,
ga_ssize outputStrides_z, int offsetY,
ga_ssize outputStrides_z, int offsetY,
int offsetZ, float* blockSum) {
int offsetZ, float* blockSum) {
// Regarding blockIdx and threadIdx, 'Cum
sum
' is always performed along the X axis.
// Regarding blockIdx and threadIdx, 'Cum
Op
' is always performed along the X axis.
// The Y and Z axis of the grid will contain all independent cum
sum
s of the 2D/3D case.
// The Y and Z axis of the grid will contain all independent cum
op
s of the 2D/3D case.
int globalThreadID = blockIdx.x * blockDim.x + threadIdx.x;
int globalThreadID = blockIdx.x * blockDim.x + threadIdx.x;
// Check if current thread has data to process.
// Check if current thread has data to process.
if (globalThreadID >=
ceil(nbElementsPerCumsum/2.0)
) {
if (globalThreadID >=
(nbElementsPerCumOp+1)/2
) {
return;
return;
}
}
extern __shared__ float partialCum
Sum
[];
extern __shared__ float partialCum
Op
[];
// Load data in shared memory
// Load data in shared memory
k_fetchData(partialCum
Sum
, input, globalThreadID, inputStrides_x, inputStrides_y, inputStrides_z, offsetY, offsetZ);
k_fetchData(partialCum
Op
, input, globalThreadID, inputStrides_x, inputStrides_y, inputStrides_z, offsetY, offsetZ);
// Use a dichotomy approach to compute the cum
sum
(i.e. balanced binary tree).
// Use a dichotomy approach to compute the cum
op
(i.e. balanced binary tree).
// The tree is sweeped from the leaves to the root and from the root to the leaves.
// The tree is sweeped from the leaves to the root and from the root to the leaves.
// Similar to http://www.umiacs.umd.edu/~ramani/cmsc828e_gpusci/ScanTalk.pdf
// Similar to http://www.umiacs.umd.edu/~ramani/cmsc828e_gpusci/ScanTalk.pdf
k_reductionPhase(partialCum
Sum
);
k_reductionPhase(partialCum
Op
);
k_reversePhase(partialCum
Sum
);
k_reversePhase(partialCum
Op
);
// Write the final output to global memory
// Write the final output to global memory
k_pushData(partialCum
Sum
, output, globalThreadID, outputStrides_x, outputStrides_y, outputStrides_z, offsetY, offsetZ);
k_pushData(partialCum
Op
, output, globalThreadID, outputStrides_x, outputStrides_y, outputStrides_z, offsetY, offsetZ);
if (blockSum != NULL){
if (blockSum != NULL){
if (threadIdx.x == blockDim.x - 1) {
if (threadIdx.x == blockDim.x - 1) {
blockSum[blockIdx.x*(gridDim.y*gridDim.z) + (blockIdx.y + offsetY)*gridDim.z + blockIdx.z + offsetZ] = partialCum
Sum
[threadIdx.x*2 + 1];
blockSum[blockIdx.x*(gridDim.y*gridDim.z) + (blockIdx.y + offsetY)*gridDim.z + blockIdx.z + offsetZ] = partialCum
Op
[threadIdx.x*2 + 1];
}
}
}
}
}
}
"""
"""
%
locals
()
kernels
.
append
(
Kernel
(
code
=
code
,
name
=
kname
,
params
=
params
,
kernels
.
append
(
Kernel
(
code
=
code
,
name
=
kname
,
params
=
params
,
flags
=
flags
,
objvar
=
k_var
))
flags
=
flags
,
objvar
=
k_var
))
# k_finalCum
Sum
# k_finalCum
Op
kname
=
"k_finalCum
Sum
"
kname
=
"k_finalCum
Op
"
k_var
=
"k_finalCum
Sum
_"
+
nodename
k_var
=
"k_finalCum
Op
_"
+
nodename
code
=
"""
code
=
"""
KERNEL void k_finalCum
Sum(float* output, float* blockSum, size_t nbElementsPerCumsum
,
KERNEL void k_finalCum
Op(float* output, float* blockSum, size_t nbElementsPerCumOp
,
ga_ssize dataStrides_x, ga_ssize dataStrides_y, ga_ssize dataStrides_z,
ga_ssize dataStrides_x, ga_ssize dataStrides_y, ga_ssize dataStrides_z,
int offsetY, int offsetZ) {
int offsetY, int offsetZ) {
int globalThreadID = (blockIdx.x + 1) * blockDim.x + threadIdx.x;
int globalThreadID = (blockIdx.x + 1) * blockDim.x + threadIdx.x;
// Check if current has data to process.
// Check if current has data to process.
if (globalThreadID >=
ceil(nbElementsPerCumsum/2.0)) {
if (globalThreadID >=
(nbElementsPerCumOp+1)/2)
return;
return;
}
int idY = blockIdx.y + offsetY;
int idY = blockIdx.y + offsetY;
int idZ = blockIdx.z + offsetZ;
int idZ = blockIdx.z + offsetZ;
...
@@ -208,10 +217,10 @@ class GpuCumsum(GpuKernelBase, Op):
...
@@ -208,10 +217,10 @@ class GpuCumsum(GpuKernelBase, Op):
int offset = idY * dataStrides_y + idZ * dataStrides_z;
int offset = idY * dataStrides_y + idZ * dataStrides_z;
int idx_even = (globalThreadID*2 ) * dataStrides_x + offset;
int idx_even = (globalThreadID*2 ) * dataStrides_x + offset;
int idx_odd = (globalThreadID*2 + 1) * dataStrides_x + offset;
int idx_odd = (globalThreadID*2 + 1) * dataStrides_x + offset;
output[idx_even]
+
= currentBlockSum;
output[idx_even]
%(op)
s
= currentBlockSum;
output[idx_odd]
+
= currentBlockSum;
output[idx_odd]
%(op)
s
= currentBlockSum;
}
}
"""
"""
%
locals
()
params
=
[
gpuarray
.
GpuArray
,
gpuarray
.
GpuArray
,
gpuarray
.
SIZE
,
params
=
[
gpuarray
.
GpuArray
,
gpuarray
.
GpuArray
,
gpuarray
.
SIZE
,
gpuarray
.
SSIZE
,
gpuarray
.
SSIZE
,
gpuarray
.
SSIZE
,
gpuarray
.
SSIZE
,
gpuarray
.
SSIZE
,
gpuarray
.
SSIZE
,
'int32'
,
'int32'
,
]
'int32'
,
'int32'
,
]
...
@@ -263,7 +272,7 @@ class GpuCumsum(GpuKernelBase, Op):
...
@@ -263,7 +272,7 @@ class GpuCumsum(GpuKernelBase, Op):
PyErr_SetString(PyExc_RuntimeError, "Could not fetch max_grid_size2");
PyErr_SetString(PyExc_RuntimeError, "Could not fetch max_grid_size2");
%(fail)
s;
%(fail)
s;
}
}
if (cum
Sum
_
%(nodename)
s(
%(x)
s,
%(z)
s, axis, max_threads_dim0, max_grid_size1, max_grid_size2) == -1){
if (cum
Op
_
%(nodename)
s(
%(x)
s,
%(z)
s, axis, max_threads_dim0, max_grid_size1, max_grid_size2) == -1){
%(fail)
s;
%(fail)
s;
}
}
}
}
...
@@ -274,7 +283,7 @@ class GpuCumsum(GpuKernelBase, Op):
...
@@ -274,7 +283,7 @@ class GpuCumsum(GpuKernelBase, Op):
def
c_support_code_struct
(
self
,
node
,
nodename
):
def
c_support_code_struct
(
self
,
node
,
nodename
):
code
=
"""
code
=
"""
int cum
Sum
_
%(nodename)
s(PyGpuArrayObject* input, PyGpuArrayObject* output, int axis, size_t maxThreads, size_t maxGridY, size_t maxGridZ) {
int cum
Op
_
%(nodename)
s(PyGpuArrayObject* input, PyGpuArrayObject* output, int axis, size_t maxThreads, size_t maxGridY, size_t maxGridZ) {
size_t shape[3] = { 1, 1, 1 };
size_t shape[3] = { 1, 1, 1 };
ssize_t inputStrides_x;
ssize_t inputStrides_x;
ssize_t inputStrides_y;
ssize_t inputStrides_y;
...
@@ -316,14 +325,14 @@ class GpuCumsum(GpuKernelBase, Op):
...
@@ -316,14 +325,14 @@ class GpuCumsum(GpuKernelBase, Op):
int err = pygpu_move(output, input);
int err = pygpu_move(output, input);
return err;
return err;
}
}
// Perform cum
sum
on array of even size.
// Perform cum
op
on array of even size.
size_t nbElementsPerCum
sum
= shape[axis] - (shape[axis]
%% 2
);
size_t nbElementsPerCum
Op
= shape[axis] - (shape[axis]
%% 2
);
// Determine how many elements can be processed in one block.
// Determine how many elements can be processed in one block.
size_t dimBlockX =
ceil((nbElementsPerCumsum > 2*maxThreads ? 2*maxThreads : nbElementsPerCumsum) / 2.0)
;
size_t dimBlockX =
((nbElementsPerCumOp > 2*maxThreads ? 2*maxThreads : nbElementsPerCumOp)+1)/2
;
// Determine how many blocks are needed in total.
// Determine how many blocks are needed in total.
size_t dimGridX =
ceil(nbElementsPerCumsum / (2.0*dimBlockX)); // Nb. of blocks needed per cumsum
.
size_t dimGridX =
(nbElementsPerCumOp+2*dimBlockX-1) / (2*dimBlockX); // Nb. of blocks needed per cum op
.
size_t dimGridY; // Nb. of independent cum
sum
s (width).
size_t dimGridY; // Nb. of independent cum
op
s (width).
size_t dimGridZ; // Nb. of independent cum
sum
s (height).
size_t dimGridZ; // Nb. of independent cum
op
s (height).
ssize_t tmp;
ssize_t tmp;
switch (axis)
switch (axis)
{
{
...
@@ -365,18 +374,18 @@ class GpuCumsum(GpuKernelBase, Op):
...
@@ -365,18 +374,18 @@ class GpuCumsum(GpuKernelBase, Op):
if (deviceBlockSum == NULL){
if (deviceBlockSum == NULL){
return -1;
return -1;
}
}
// Perform `maxGridY`*`maxGridZ` cum
sum
s in parallel.
// Perform `maxGridY`*`maxGridZ` cum
op
s in parallel.
for (size_t offsetY = 0; offsetY < dimGridY; offsetY += maxGridY){
for (size_t offsetY = 0; offsetY < dimGridY; offsetY += maxGridY){
size_t localDimGridY = (dimGridY - offsetY < maxGridY) ? (dimGridY - offsetY) : (maxGridY);
size_t localDimGridY = (dimGridY - offsetY < maxGridY) ? (dimGridY - offsetY) : (maxGridY);
for (size_t offsetZ = 0; offsetZ < dimGridZ; offsetZ += maxGridZ){
for (size_t offsetZ = 0; offsetZ < dimGridZ; offsetZ += maxGridZ){
size_t localDimGridZ = (dimGridZ - offsetZ < maxGridZ) ? (dimGridZ - offsetZ) : (maxGridZ);
size_t localDimGridZ = (dimGridZ - offsetZ < maxGridZ) ? (dimGridZ - offsetZ) : (maxGridZ);
size_t dimGrid[3] = {dimGridX, localDimGridY, localDimGridZ};
size_t dimGrid[3] = {dimGridX, localDimGridY, localDimGridZ};
size_t dimBlock[3] = {dimBlockX, 1, 1}; // One cum
sum
per block.
size_t dimBlock[3] = {dimBlockX, 1, 1}; // One cum
op
per block.
size_t sharedBytes = (2*dimBlockX) * sizeof(float);
size_t sharedBytes = (2*dimBlockX) * sizeof(float);
void* kernel_params[] = {(void*) input->ga.data,
void* kernel_params[] = {(void*) input->ga.data,
(void*) output->ga.data,
(void*) output->ga.data,
(void*) &nbElementsPerCum
sum
,
(void*) &nbElementsPerCum
Op
,
(void*) &inputStrides_x,
(void*) &inputStrides_x,
(void*) &inputStrides_y,
(void*) &inputStrides_y,
(void*) &inputStrides_z,
(void*) &inputStrides_z,
...
@@ -387,39 +396,39 @@ class GpuCumsum(GpuKernelBase, Op):
...
@@ -387,39 +396,39 @@ class GpuCumsum(GpuKernelBase, Op):
(void*) &offsetZ,
(void*) &offsetZ,
(void*) deviceBlockSum->ga.data
(void*) deviceBlockSum->ga.data
};
};
int err = GpuKernel_call(&k_blockCum
Sum
_
%(nodename)
s, 3, dimBlock, dimGrid, sharedBytes, kernel_params);
int err = GpuKernel_call(&k_blockCum
Op
_
%(nodename)
s, 3, dimBlock, dimGrid, sharedBytes, kernel_params);
if (err != GA_NO_ERROR){
if (err != GA_NO_ERROR){
PyErr_SetString(PyExc_RuntimeError, "blockCum
Sum
call failed");
PyErr_SetString(PyExc_RuntimeError, "blockCum
Op
call failed");
return -1;
return -1;
}
}
if (dimGridX > 1) {
if (dimGridX > 1) {
// Do a cum
sum
over the blockSum (recursive).
// Do a cum
op
over the blockSum (recursive).
if (cum
Sum
_
%(nodename)
s(deviceBlockSum, deviceBlockSum, 0, maxThreads, maxGridY, maxGridZ) == -1){
if (cum
Op
_
%(nodename)
s(deviceBlockSum, deviceBlockSum, 0, maxThreads, maxGridY, maxGridZ) == -1){
Py_DECREF(deviceBlockSum);
Py_DECREF(deviceBlockSum);
return -1;
return -1;
}
}
// Since there are more than one block (i.e. `dimGridX > 1`)
// Since there are more than one block (i.e. `dimGridX > 1`)
// report partial cum
sum
s of previous blocks to subsequents ones.
// report partial cum
op
s of previous blocks to subsequents ones.
size_t dimGrid[3] = {dimGridX, localDimGridY, localDimGridZ};
size_t dimGrid[3] = {dimGridX, localDimGridY, localDimGridZ};
size_t dimBlock[3] = {dimBlockX, 1, 1};
size_t dimBlock[3] = {dimBlockX, 1, 1};
void* kernel_params[] = {(void*) output->ga.data,
void* kernel_params[] = {(void*) output->ga.data,
(void*) deviceBlockSum->ga.data,
(void*) deviceBlockSum->ga.data,
(void*) &nbElementsPerCum
sum
,
(void*) &nbElementsPerCum
Op
,
(void*) &outputStrides_x,
(void*) &outputStrides_x,
(void*) &outputStrides_y,
(void*) &outputStrides_y,
(void*) &outputStrides_z,
(void*) &outputStrides_z,
(void*) &offsetY,
(void*) &offsetY,
(void*) &offsetZ
(void*) &offsetZ
};
};
int err = GpuKernel_call(&k_finalCum
Sum
_
%(nodename)
s, 3, dimBlock, dimGrid, sharedBytes, kernel_params);
int err = GpuKernel_call(&k_finalCum
Op
_
%(nodename)
s, 3, dimBlock, dimGrid, sharedBytes, kernel_params);
if (err != GA_NO_ERROR){
if (err != GA_NO_ERROR){
PyErr_SetString(PyExc_RuntimeError, "finalCum
Sum
call failed");
PyErr_SetString(PyExc_RuntimeError, "finalCum
Op
call failed");
return -1;
return -1;
}
}
}
}
// If shape[axis] is odd, the last element is compute manually
// If shape[axis] is odd, the last element is compute manually
if (shape[axis] != nbElementsPerCum
sum
){
if (shape[axis] != nbElementsPerCum
Op
){
size_t dimGrid[3] = {1, localDimGridY, localDimGridZ};
size_t dimGrid[3] = {1, localDimGridY, localDimGridZ};
size_t dimBlock[3] = {1, 1, 1};
size_t dimBlock[3] = {1, 1, 1};
size_t tmp0 = shape[axis]-2;
size_t tmp0 = shape[axis]-2;
...
@@ -450,26 +459,39 @@ class GpuCumsum(GpuKernelBase, Op):
...
@@ -450,26 +459,39 @@ class GpuCumsum(GpuKernelBase, Op):
return 0;
return 0;
}
}
"""
%
locals
()
"""
%
locals
()
return
super
(
GpuCum
sum
,
self
)
.
c_support_code_struct
(
node
,
nodename
)
+
code
return
super
(
GpuCum
Op
,
self
)
.
c_support_code_struct
(
node
,
nodename
)
+
code
@register_opt
(
'fast_compile'
)
# GpuCumsumOp exists only to serve backward compatibility.
@op_lifter
([
CumsumOp
])
# Once an object is created, it will be converted to CumOp object.
@register_opt2
([
CumsumOp
],
'fast_compile'
)
class
GpuCumsumOp
(
GpuKernelBase
,
Op
):
def
local_gpua_cumsumop
(
op
,
ctx_name
,
inputs
,
outputs
):
SUPPORTED_NDIMS
=
3
if
inputs
[
0
]
.
dtype
==
'float32'
:
__props__
=
(
"axis"
,)
axis
=
op
.
axis
x
=
inputs
[
0
]
if
axis
is
not
None
and
x
.
ndim
>
GpuCumsum
.
SUPPORTED_NDIMS
:
return
None
x
=
as_gpuarray_variable
(
x
,
ctx_name
)
if
axis
is
None
and
x
.
ndim
>
1
:
def
__new__
(
typ
,
*
args
,
**
kwargs
):
x
=
GpuReshape
(
1
)(
x
,
(
-
1
,))
obj
=
object
.
__new__
(
GpuCumOp
,
*
args
,
**
kwargs
)
obj
.
mode
=
'add'
return
obj
# ``gpu_cumsum`` assume array has been flattened if needed.
if
axis
is
None
:
axis
=
0
return
GpuCumsum
(
axis
)(
x
)
@register_opt
(
'fast_compile'
)
@op_lifter
([
CumOp
])
@register_opt2
([
CumOp
],
'fast_compile'
)
def
local_gpua_cumop
(
op
,
ctx_name
,
inputs
,
outputs
):
if
inputs
[
0
]
.
dtype
!=
'float32'
:
return
False
axis
=
op
.
axis
x
=
inputs
[
0
]
if
axis
is
not
None
and
x
.
ndim
>
GpuCumOp
.
SUPPORTED_NDIMS
:
return
False
x
=
as_gpuarray_variable
(
x
,
ctx_name
)
if
axis
is
None
and
x
.
ndim
>
1
:
x
=
GpuReshape
(
1
)(
x
,
(
-
1
,))
# ``gpu_cumop`` assume array has been flattened if needed.
if
axis
is
None
:
axis
=
0
return
GpuCumOp
(
axis
,
op
.
mode
)(
x
)
theano/gpuarray/tests/test_extra_ops.py
浏览文件 @
90dd93d0
# Skip test if cuda_ndarray is not available.
from
__future__
import
absolute_import
,
print_function
,
division
from
__future__
import
absolute_import
,
print_function
,
division
import
itertools
from
functools
import
partial
from
itertools
import
product
import
numpy
as
np
import
numpy
as
np
from
six.moves
import
xrange
from
six.moves
import
xrange
...
@@ -9,54 +9,62 @@ from theano import tensor as T
...
@@ -9,54 +9,62 @@ from theano import tensor as T
import
theano
import
theano
import
theano.tensor.tests.test_extra_ops
import
theano.tensor.tests.test_extra_ops
from
theano.tensor.extra_ops
import
cumsum
,
Cums
umOp
from
theano.tensor.extra_ops
import
C
umOp
from
theano.tests.unittest_tools
import
SkipTest
from
theano.tests.unittest_tools
import
SkipTest
from
theano.tests
import
unittest_tools
as
utt
from
theano.tests
import
unittest_tools
as
utt
from
.config
import
mode_with_gpu
,
test_ctx_name
from
.config
import
mode_with_gpu
,
test_ctx_name
from
..extra_ops
import
GpuCum
sum
from
..extra_ops
import
GpuCum
Op
from
..type
import
get_context
from
..type
import
get_context
cum_modes
=
utt
.
parameterized
.
expand
([(
'mul'
,),
(
'add'
,)])
class
TestGpuCumsum
(
theano
.
tensor
.
tests
.
test_extra_ops
.
TestCumsumOp
):
class
TestGpuCumOp
(
theano
.
tensor
.
tests
.
test_extra_ops
.
TestCumOp
):
mode
=
mode_with_gpu
mode
=
mode_with_gpu
def
setUp
(
self
):
def
setUp
(
self
):
super
(
TestGpuCum
sum
,
self
)
.
setUp
()
super
(
TestGpuCum
Op
,
self
)
.
setUp
()
test_ctx
=
get_context
(
test_ctx_name
)
test_ctx
=
get_context
(
test_ctx_name
)
if
test_ctx
.
kind
!=
b
'cuda'
:
if
test_ctx
.
kind
!=
b
'cuda'
:
raise
SkipTest
(
"Cuda specific tests"
)
raise
SkipTest
(
"Cuda specific tests"
)
self
.
max_threads_dim0
=
test_ctx
.
maxlsize0
self
.
max_threads_dim0
=
test_ctx
.
maxlsize0
self
.
max_grid_size1
=
test_ctx
.
maxgsize2
self
.
max_grid_size1
=
test_ctx
.
maxgsize2
self
.
op_class
=
GpuCumsum
self
.
op_class
=
CumOp
def
test_infer_shape
(
self
):
@cum_modes
# GpuCumSum is only defined for float32 for now, so we skip it
def
test_infer_shape
(
self
,
mode
):
# GpuCumOp is only defined for float32 for now, so we skip it
# in the unsupported cases
# in the unsupported cases
gpucumsum_supported_dtypes
=
(
'float32'
,)
op_class
=
partial
(
self
.
op_class
,
mode
=
mode
)
if
theano
.
config
.
floatX
not
in
gpucumsum_supported_dtypes
:
gpucumop_supported_dtypes
=
(
'float32'
,)
raise
SkipTest
(
'GpuCumSum not implemented for dtype
%
s'
if
theano
.
config
.
floatX
not
in
gpucumop_supported_dtypes
:
raise
SkipTest
(
'Gpucumop not implemented for dtype
%
s'
%
theano
.
config
.
floatX
)
%
theano
.
config
.
floatX
)
x
=
T
.
tensor3
(
'x'
)
x
=
T
.
tensor3
(
'x'
)
a
=
np
.
random
.
random
((
3
,
5
,
2
))
.
astype
(
theano
.
config
.
floatX
)
a
=
np
.
random
.
random
((
3
,
5
,
2
))
.
astype
(
theano
.
config
.
floatX
)
for
axis
in
range
(
-
len
(
a
.
shape
),
len
(
a
.
shape
)):
for
axis
in
range
(
-
len
(
a
.
shape
),
len
(
a
.
shape
)):
self
.
_compile_and_check
([
x
],
self
.
_compile_and_check
([
x
],
[
cumsum
(
x
,
axis
=
axis
)],
[
op_class
(
axis
=
axis
)(
x
)],
[
a
],
[
a
],
self
.
op_class
)
GpuCumOp
)
def
test_grad
(
self
):
@cum_modes
# no grad for GpuCumsum
def
test_grad
(
self
,
mode
):
# no grad for GpuCumOp
pass
pass
def
test_Strides1D
(
self
):
@cum_modes
def
test_Strides1D
(
self
,
mode
):
op_class
=
partial
(
self
.
op_class
,
mode
=
mode
)
np_func
=
dict
(
add
=
np
.
cumsum
,
mul
=
np
.
cumprod
)[
mode
]
x
=
T
.
fvector
(
'x'
)
x
=
T
.
fvector
(
'x'
)
for
axis
in
[
0
,
None
,
-
1
]:
for
axis
in
[
0
,
None
,
-
1
]:
a
=
np
.
random
.
random
((
42
,))
.
astype
(
"float32"
)
a
=
np
.
random
.
random
((
42
,))
.
astype
(
"float32"
)
cum
sum_function
=
theano
.
function
([
x
],
cumsum
(
x
,
axis
=
axis
),
cum
op_function
=
theano
.
function
(
mode
=
self
.
mode
)
[
x
],
op_class
(
axis
=
axis
)(
x
),
mode
=
self
.
mode
)
slicings
=
[
slice
(
None
,
None
,
None
),
# Normal strides
slicings
=
[
slice
(
None
,
None
,
None
),
# Normal strides
slice
(
None
,
None
,
2
),
# Stepped strides
slice
(
None
,
None
,
2
),
# Stepped strides
...
@@ -64,22 +72,25 @@ class TestGpuCumsum(theano.tensor.tests.test_extra_ops.TestCumsumOp):
...
@@ -64,22 +72,25 @@ class TestGpuCumsum(theano.tensor.tests.test_extra_ops.TestCumsumOp):
]
]
# Cartesian product of all slicings to test.
# Cartesian product of all slicings to test.
for
slicing
in
itertools
.
product
(
slicings
,
repeat
=
x
.
ndim
):
for
slicing
in
product
(
slicings
,
repeat
=
x
.
ndim
):
f
=
theano
.
function
([
x
],
cumsum
(
x
[
slicing
],
axis
=
axis
),
f
=
theano
.
function
([
x
],
op_class
(
axis
=
axis
)(
x
[
slicing
]
),
mode
=
self
.
mode
)
mode
=
self
.
mode
)
assert
[
n
for
n
in
f
.
maker
.
fgraph
.
toposort
()
assert
[
n
for
n
in
f
.
maker
.
fgraph
.
toposort
()
if
isinstance
(
n
.
op
,
GpuCumsum
)]
if
isinstance
(
n
.
op
,
GpuCumOp
)]
utt
.
assert_allclose
(
np
.
cumsum
(
a
[
slicing
],
axis
=
axis
),
f
(
a
))
utt
.
assert_allclose
(
np_func
(
a
[
slicing
],
axis
=
axis
),
f
(
a
))
utt
.
assert_allclose
(
np
.
cumsum
(
a
[
slicing
],
axis
=
axis
),
utt
.
assert_allclose
(
np_func
(
a
[
slicing
],
axis
=
axis
),
cumsum_function
(
a
[
slicing
]))
cumop_function
(
a
[
slicing
]))
def
test_Strides2D
(
self
):
@cum_modes
def
test_Strides2D
(
self
,
mode
):
np_func
=
dict
(
add
=
np
.
cumsum
,
mul
=
np
.
cumprod
)[
mode
]
op_class
=
partial
(
self
.
op_class
,
mode
=
mode
)
x
=
T
.
fmatrix
(
'x'
)
x
=
T
.
fmatrix
(
'x'
)
for
axis
in
[
0
,
1
,
None
,
-
1
,
-
2
]:
for
axis
in
[
0
,
1
,
None
,
-
1
,
-
2
]:
a
=
np
.
random
.
random
((
42
,
30
))
.
astype
(
"float32"
)
a
=
np
.
random
.
random
((
42
,
30
))
.
astype
(
"float32"
)
cum
sum_function
=
theano
.
function
([
x
],
cumsum
(
x
,
axis
=
axis
),
cum
op_function
=
theano
.
function
(
mode
=
self
.
mode
)
[
x
],
op_class
(
axis
=
axis
)(
x
),
mode
=
self
.
mode
)
slicings
=
[
slice
(
None
,
None
,
None
),
# Normal strides
slicings
=
[
slice
(
None
,
None
,
None
),
# Normal strides
slice
(
None
,
None
,
2
),
# Stepped strides
slice
(
None
,
None
,
2
),
# Stepped strides
...
@@ -87,22 +98,25 @@ class TestGpuCumsum(theano.tensor.tests.test_extra_ops.TestCumsumOp):
...
@@ -87,22 +98,25 @@ class TestGpuCumsum(theano.tensor.tests.test_extra_ops.TestCumsumOp):
]
]
# Cartesian product of all slicings to test.
# Cartesian product of all slicings to test.
for
slicing
in
itertools
.
product
(
slicings
,
repeat
=
x
.
ndim
):
for
slicing
in
product
(
slicings
,
repeat
=
x
.
ndim
):
f
=
theano
.
function
([
x
],
cumsum
(
x
[
slicing
],
axis
=
axis
),
f
=
theano
.
function
([
x
],
op_class
(
axis
=
axis
)(
x
[
slicing
]
),
mode
=
self
.
mode
)
mode
=
self
.
mode
)
assert
[
n
for
n
in
f
.
maker
.
fgraph
.
toposort
()
assert
[
n
for
n
in
f
.
maker
.
fgraph
.
toposort
()
if
isinstance
(
n
.
op
,
GpuCumsum
)]
if
isinstance
(
n
.
op
,
GpuCumOp
)]
utt
.
assert_allclose
(
np
.
cumsum
(
a
[
slicing
],
axis
=
axis
),
f
(
a
))
utt
.
assert_allclose
(
np_func
(
a
[
slicing
],
axis
=
axis
),
f
(
a
))
utt
.
assert_allclose
(
np
.
cumsum
(
a
[
slicing
],
axis
=
axis
),
utt
.
assert_allclose
(
np_func
(
a
[
slicing
],
axis
=
axis
),
cumsum_function
(
a
[
slicing
]))
cumop_function
(
a
[
slicing
]))
def
test_Strides3D
(
self
):
@cum_modes
def
test_Strides3D
(
self
,
mode
):
np_func
=
dict
(
add
=
np
.
cumsum
,
mul
=
np
.
cumprod
)[
mode
]
op_class
=
partial
(
self
.
op_class
,
mode
=
mode
)
x
=
T
.
ftensor3
(
'x'
)
x
=
T
.
ftensor3
(
'x'
)
for
axis
in
[
0
,
1
,
2
,
None
,
-
1
,
-
2
,
-
3
]:
for
axis
in
[
0
,
1
,
2
,
None
,
-
1
,
-
2
,
-
3
]:
a
=
np
.
random
.
random
((
42
,
30
,
25
))
.
astype
(
"float32"
)
a
=
np
.
random
.
random
((
42
,
30
,
25
))
.
astype
(
"float32"
)
cum
sum_function
=
theano
.
function
([
x
],
cumsum
(
x
,
axis
=
axis
),
cum
op_function
=
theano
.
function
(
mode
=
self
.
mode
)
[
x
],
op_class
(
axis
=
axis
)(
x
),
mode
=
self
.
mode
)
slicings
=
[
slice
(
None
,
None
,
None
),
# Normal strides
slicings
=
[
slice
(
None
,
None
,
None
),
# Normal strides
slice
(
None
,
None
,
2
),
# Stepped strides
slice
(
None
,
None
,
2
),
# Stepped strides
...
@@ -110,45 +124,51 @@ class TestGpuCumsum(theano.tensor.tests.test_extra_ops.TestCumsumOp):
...
@@ -110,45 +124,51 @@ class TestGpuCumsum(theano.tensor.tests.test_extra_ops.TestCumsumOp):
]
]
# Cartesian product of all slicings to test.
# Cartesian product of all slicings to test.
for
slicing
in
itertools
.
product
(
slicings
,
repeat
=
x
.
ndim
):
for
slicing
in
product
(
slicings
,
repeat
=
x
.
ndim
):
f
=
theano
.
function
(
[
x
],
cumsum
(
x
[
slicing
],
axis
=
axis
),
f
=
theano
.
function
(
mode
=
self
.
mode
)
[
x
],
op_class
(
axis
=
axis
)(
x
[
slicing
]),
mode
=
self
.
mode
)
assert
[
n
for
n
in
f
.
maker
.
fgraph
.
toposort
()
assert
[
n
for
n
in
f
.
maker
.
fgraph
.
toposort
()
if
isinstance
(
n
.
op
,
GpuCumsum
)]
if
isinstance
(
n
.
op
,
GpuCumOp
)]
utt
.
assert_allclose
(
np
.
cumsum
(
a
[
slicing
],
axis
=
axis
),
f
(
a
))
utt
.
assert_allclose
(
np_func
(
a
[
slicing
],
axis
=
axis
),
f
(
a
))
utt
.
assert_allclose
(
np
.
cumsum
(
a
[
slicing
],
axis
=
axis
),
utt
.
assert_allclose
(
np_func
(
a
[
slicing
],
axis
=
axis
),
cumsum_function
(
a
[
slicing
]))
cumop_function
(
a
[
slicing
]))
def
test_GpuCumsum1D
(
self
):
@cum_modes
def
test_GpuCumOp1D
(
self
,
mode
):
np_func
=
dict
(
add
=
np
.
cumsum
,
mul
=
np
.
cumprod
)[
mode
]
op_class
=
partial
(
self
.
op_class
,
mode
=
mode
)
block_max_size
=
self
.
max_threads_dim0
*
2
block_max_size
=
self
.
max_threads_dim0
*
2
x
=
T
.
fvector
(
'x'
)
x
=
T
.
fvector
(
'x'
)
f
=
theano
.
function
([
x
],
cumsum
(
x
),
mode
=
self
.
mode
)
f
=
theano
.
function
([
x
],
op_class
(
axis
=
0
)
(
x
),
mode
=
self
.
mode
)
assert
[
n
for
n
in
f
.
maker
.
fgraph
.
toposort
()
assert
[
n
for
n
in
f
.
maker
.
fgraph
.
toposort
()
if
isinstance
(
n
.
op
,
GpuCum
sum
)]
if
isinstance
(
n
.
op
,
GpuCum
Op
)]
# Extensive testing for the first 1025 sizes
# Extensive testing for the first 1025 sizes
a
=
np
.
random
.
random
(
1025
)
.
astype
(
"float32"
)
a
=
np
.
random
.
random
(
1025
)
.
astype
(
"float32"
)
for
i
in
xrange
(
a
.
shape
[
0
]):
for
i
in
xrange
(
a
.
shape
[
0
]):
utt
.
assert_allclose
(
np
.
cumsum
(
a
[:
i
]),
f
(
a
[:
i
]))
utt
.
assert_allclose
(
np
_func
(
a
[:
i
]),
f
(
a
[:
i
]))
# Use multiple GPU threadblocks
# Use multiple GPU threadblocks
a
=
np
.
random
.
random
((
block_max_size
+
2
,
))
.
astype
(
"float32"
)
a
=
np
.
random
.
random
((
block_max_size
+
2
,
))
.
astype
(
"float32"
)
utt
.
assert_allclose
(
np
.
cumsum
(
a
),
f
(
a
))
utt
.
assert_allclose
(
np
_func
(
a
),
f
(
a
))
# Use recursive cum
sum
# Use recursive cum
op
a
=
np
.
ones
((
block_max_size
*
(
block_max_size
+
1
)
+
2
,),
a
=
np
.
ones
((
block_max_size
*
(
block_max_size
+
1
)
+
2
,),
dtype
=
"float32"
)
dtype
=
"float32"
)
utt
.
assert_allclose
(
np
.
cumsum
(
a
),
f
(
a
))
utt
.
assert_allclose
(
np
_func
(
a
),
f
(
a
))
def
test_GpuCumsum2D
(
self
):
@cum_modes
def
test_GpuCumOp2D
(
self
,
mode
):
np_func
=
dict
(
add
=
np
.
cumsum
,
mul
=
np
.
cumprod
)[
mode
]
op_class
=
partial
(
self
.
op_class
,
mode
=
mode
)
block_max_size
=
self
.
max_threads_dim0
*
2
block_max_size
=
self
.
max_threads_dim0
*
2
x
=
T
.
fmatrix
(
'x'
)
x
=
T
.
fmatrix
(
'x'
)
for
shape_axis
,
axis
in
zip
([
0
,
1
,
0
,
1
,
0
],
[
0
,
1
,
None
,
-
1
,
-
2
]):
for
shape_axis
,
axis
in
zip
([
0
,
1
,
0
,
1
,
0
],
[
0
,
1
,
None
,
-
1
,
-
2
]):
f
=
theano
.
function
([
x
],
cumsum
(
x
,
axis
=
axis
),
mode
=
self
.
mode
)
f
=
theano
.
function
([
x
],
op_class
(
axis
=
axis
)(
x
),
mode
=
self
.
mode
)
assert
[
n
for
n
in
f
.
maker
.
fgraph
.
toposort
()
assert
[
n
for
n
in
f
.
maker
.
fgraph
.
toposort
()
if
isinstance
(
n
.
op
,
GpuCum
sum
)]
if
isinstance
(
n
.
op
,
GpuCum
Op
)]
# Extensive testing for the first 1025 sizes
# Extensive testing for the first 1025 sizes
a_shape
=
[
5
,
5
]
a_shape
=
[
5
,
5
]
...
@@ -158,36 +178,39 @@ class TestGpuCumsum(theano.tensor.tests.test_extra_ops.TestCumsumOp):
...
@@ -158,36 +178,39 @@ class TestGpuCumsum(theano.tensor.tests.test_extra_ops.TestCumsumOp):
for
i
in
xrange
(
a
.
shape
[
shape_axis
]):
for
i
in
xrange
(
a
.
shape
[
shape_axis
]):
slices
[
shape_axis
]
=
slice
(
i
)
slices
[
shape_axis
]
=
slice
(
i
)
fa
=
f
(
a
[
slices
])
fa
=
f
(
a
[
slices
])
npa
=
np
.
cumsum
(
a
[
slices
],
axis
=
axis
)
npa
=
np
_func
(
a
[
slices
],
axis
=
axis
)
utt
.
assert_allclose
(
npa
,
fa
)
utt
.
assert_allclose
(
npa
,
fa
)
# Use multiple GPU threadblocks
# Use multiple GPU threadblocks
a_shape
=
[
5
,
5
]
a_shape
=
[
5
,
5
]
a_shape
[
shape_axis
]
=
block_max_size
+
2
a_shape
[
shape_axis
]
=
block_max_size
+
2
a
=
np
.
random
.
random
(
a_shape
)
.
astype
(
"float32"
)
a
=
np
.
random
.
random
(
a_shape
)
.
astype
(
"float32"
)
utt
.
assert_allclose
(
np
.
cumsum
(
a
,
axis
=
axis
),
f
(
a
))
utt
.
assert_allclose
(
np
_func
(
a
,
axis
=
axis
),
f
(
a
))
# Use multiple GPU gridblocks
# Use multiple GPU gridblocks
a_shape
=
[
4
,
4
]
a_shape
=
[
4
,
4
]
a_shape
[
1
-
shape_axis
]
=
self
.
max_grid_size1
+
1
a_shape
[
1
-
shape_axis
]
=
self
.
max_grid_size1
+
1
a
=
np
.
random
.
random
(
a_shape
)
.
astype
(
"float32"
)
a
=
np
.
random
.
random
(
a_shape
)
.
astype
(
"float32"
)
utt
.
assert_allclose
(
np
.
cumsum
(
a
,
axis
=
axis
),
f
(
a
),
rtol
=
5e-5
)
utt
.
assert_allclose
(
np
_func
(
a
,
axis
=
axis
),
f
(
a
),
rtol
=
5e-5
)
# Use recursive cum
sum
# Use recursive cum
op
a_shape
=
[
3
,
3
]
a_shape
=
[
3
,
3
]
a_shape
[
shape_axis
]
=
block_max_size
*
(
block_max_size
+
1
)
+
2
a_shape
[
shape_axis
]
=
block_max_size
*
(
block_max_size
+
1
)
+
2
a
=
np
.
random
.
random
(
a_shape
)
.
astype
(
"float32"
)
a
=
np
.
random
.
random
(
a_shape
)
.
astype
(
"float32"
)
a
=
np
.
sign
(
a
-
0.5
)
.
astype
(
"float32"
)
# Avoid floating point error
a
=
np
.
sign
(
a
-
0.5
)
.
astype
(
"float32"
)
# Avoid floating point error
utt
.
assert_allclose
(
np
.
cumsum
(
a
,
axis
=
axis
),
f
(
a
))
utt
.
assert_allclose
(
np
_func
(
a
,
axis
=
axis
),
f
(
a
))
def
test_GpuCumsum3D
(
self
):
@cum_modes
def
test_GpuCumOp3D
(
self
,
mode
):
np_func
=
dict
(
add
=
np
.
cumsum
,
mul
=
np
.
cumprod
)[
mode
]
op_class
=
partial
(
self
.
op_class
,
mode
=
mode
)
block_max_size
=
self
.
max_threads_dim0
*
2
block_max_size
=
self
.
max_threads_dim0
*
2
x
=
T
.
ftensor3
(
'x'
)
x
=
T
.
ftensor3
(
'x'
)
for
shape_axis
,
axis
in
zip
([
0
,
1
,
2
,
0
,
2
,
1
,
0
],
[
0
,
1
,
2
,
None
,
-
1
,
-
2
,
-
3
]):
for
shape_axis
,
axis
in
zip
([
0
,
1
,
2
,
0
,
2
,
1
,
0
],
[
0
,
1
,
2
,
None
,
-
1
,
-
2
,
-
3
]):
f
=
theano
.
function
([
x
],
cumsum
(
x
,
axis
=
axis
),
mode
=
self
.
mode
)
f
=
theano
.
function
([
x
],
op_class
(
axis
=
axis
)(
x
),
mode
=
self
.
mode
)
assert
[
n
for
n
in
f
.
maker
.
fgraph
.
toposort
()
assert
[
n
for
n
in
f
.
maker
.
fgraph
.
toposort
()
if
isinstance
(
n
.
op
,
GpuCum
sum
)]
if
isinstance
(
n
.
op
,
GpuCum
Op
)]
# Extensive testing for the first 1025 sizes
# Extensive testing for the first 1025 sizes
a_shape
=
[
5
,
5
,
5
]
a_shape
=
[
5
,
5
,
5
]
...
@@ -197,14 +220,14 @@ class TestGpuCumsum(theano.tensor.tests.test_extra_ops.TestCumsumOp):
...
@@ -197,14 +220,14 @@ class TestGpuCumsum(theano.tensor.tests.test_extra_ops.TestCumsumOp):
for
i
in
xrange
(
a
.
shape
[
shape_axis
]):
for
i
in
xrange
(
a
.
shape
[
shape_axis
]):
slices
[
shape_axis
]
=
slice
(
i
)
slices
[
shape_axis
]
=
slice
(
i
)
fa
=
f
(
a
[
slices
])
fa
=
f
(
a
[
slices
])
npa
=
np
.
cumsum
(
a
[
slices
],
axis
=
axis
)
npa
=
np
_func
(
a
[
slices
],
axis
=
axis
)
utt
.
assert_allclose
(
npa
,
fa
)
utt
.
assert_allclose
(
npa
,
fa
)
# Use multiple GPU threadblocks (along accumulation axis)
# Use multiple GPU threadblocks (along accumulation axis)
a_shape
=
[
2
,
2
,
2
]
a_shape
=
[
2
,
2
,
2
]
a_shape
[
shape_axis
]
=
block_max_size
+
2
a_shape
[
shape_axis
]
=
block_max_size
+
2
a
=
np
.
random
.
random
(
a_shape
)
.
astype
(
"float32"
)
a
=
np
.
random
.
random
(
a_shape
)
.
astype
(
"float32"
)
utt
.
assert_allclose
(
np
.
cumsum
(
a
,
axis
=
axis
),
f
(
a
))
utt
.
assert_allclose
(
np
_func
(
a
,
axis
=
axis
),
f
(
a
))
# Use multiple GPU gridblocks (not along accumulation axis)
# Use multiple GPU gridblocks (not along accumulation axis)
a_shape
=
[
5
,
5
,
5
]
a_shape
=
[
5
,
5
,
5
]
...
@@ -213,7 +236,7 @@ class TestGpuCumsum(theano.tensor.tests.test_extra_ops.TestCumsumOp):
...
@@ -213,7 +236,7 @@ class TestGpuCumsum(theano.tensor.tests.test_extra_ops.TestCumsumOp):
if
axis
is
None
:
if
axis
is
None
:
# Avoid floating point error
# Avoid floating point error
a
=
np
.
sign
(
a
-
0.5
)
.
astype
(
"float32"
)
a
=
np
.
sign
(
a
-
0.5
)
.
astype
(
"float32"
)
utt
.
assert_allclose
(
np
.
cumsum
(
a
,
axis
=
axis
),
f
(
a
))
utt
.
assert_allclose
(
np
_func
(
a
,
axis
=
axis
),
f
(
a
))
a_shape
=
[
5
,
5
,
5
]
a_shape
=
[
5
,
5
,
5
]
a_shape
[(
shape_axis
+
2
)
%
3
]
=
self
.
max_grid_size1
+
1
a_shape
[(
shape_axis
+
2
)
%
3
]
=
self
.
max_grid_size1
+
1
...
@@ -221,18 +244,20 @@ class TestGpuCumsum(theano.tensor.tests.test_extra_ops.TestCumsumOp):
...
@@ -221,18 +244,20 @@ class TestGpuCumsum(theano.tensor.tests.test_extra_ops.TestCumsumOp):
if
axis
is
None
:
if
axis
is
None
:
# Avoid floating point error
# Avoid floating point error
a
=
np
.
sign
(
a
-
0.5
)
.
astype
(
"float32"
)
a
=
np
.
sign
(
a
-
0.5
)
.
astype
(
"float32"
)
utt
.
assert_allclose
(
np
.
cumsum
(
a
,
axis
=
axis
),
f
(
a
))
utt
.
assert_allclose
(
np
_func
(
a
,
axis
=
axis
),
f
(
a
))
# Use recursive cum
sum
(along accumulation axis)
# Use recursive cum
op
(along accumulation axis)
a_shape
=
[
3
,
3
,
3
]
a_shape
=
[
3
,
3
,
3
]
a_shape
[
shape_axis
]
=
block_max_size
*
(
block_max_size
+
1
)
+
2
a_shape
[
shape_axis
]
=
block_max_size
*
(
block_max_size
+
1
)
+
2
a
=
np
.
random
.
random
(
a_shape
)
.
astype
(
"float32"
)
a
=
np
.
random
.
random
(
a_shape
)
.
astype
(
"float32"
)
a
=
np
.
sign
(
a
-
0.5
)
.
astype
(
"float32"
)
# Avoid floating point error
a
=
np
.
sign
(
a
-
0.5
)
.
astype
(
"float32"
)
# Avoid floating point error
utt
.
assert_allclose
(
np
.
cumsum
(
a
,
axis
=
axis
),
f
(
a
))
utt
.
assert_allclose
(
np
_func
(
a
,
axis
=
axis
),
f
(
a
))
def
test_GpuCumsum4D
(
self
):
@cum_modes
def
test_GpuCumOp4D
(
self
,
mode
):
op_class
=
partial
(
self
.
op_class
,
mode
=
mode
)
# Should not use the GPU version.
# Should not use the GPU version.
x
=
T
.
ftensor4
(
'x'
)
x
=
T
.
ftensor4
(
'x'
)
f
=
theano
.
function
([
x
],
cumsum
(
x
,
axis
=
1
),
mode
=
self
.
mode
)
f
=
theano
.
function
([
x
],
op_class
(
axis
=
1
)(
x
),
mode
=
self
.
mode
)
assert
[
n
for
n
in
f
.
maker
.
fgraph
.
toposort
()
assert
[
n
for
n
in
f
.
maker
.
fgraph
.
toposort
()
if
isinstance
(
n
.
op
,
Cums
umOp
)]
if
isinstance
(
n
.
op
,
GpuC
umOp
)]
theano/sandbox/cuda/extra_ops.py
浏览文件 @
90dd93d0
...
@@ -5,7 +5,7 @@ from theano import Op
...
@@ -5,7 +5,7 @@ from theano import Op
from
theano.gof
import
local_optimizer
from
theano.gof
import
local_optimizer
from
theano.sandbox.cuda
import
cuda_available
,
GpuOp
from
theano.sandbox.cuda
import
cuda_available
,
GpuOp
from
theano.sandbox.cuda.basic_ops
import
gpu_flatten
from
theano.sandbox.cuda.basic_ops
import
gpu_flatten
from
theano.tensor.extra_ops
import
Cum
sum
Op
from
theano.tensor.extra_ops
import
CumOp
if
cuda_available
:
if
cuda_available
:
from
theano.sandbox.cuda
import
CudaNdarrayType
from
theano.sandbox.cuda
import
CudaNdarrayType
...
@@ -13,7 +13,7 @@ if cuda_available:
...
@@ -13,7 +13,7 @@ if cuda_available:
from
theano.sandbox.cuda
import
register_opt
as
register_gpu_opt
from
theano.sandbox.cuda
import
register_opt
as
register_gpu_opt
class
GpuCumsum
(
Cum
sum
Op
,
GpuOp
):
class
GpuCumsum
(
CumOp
,
GpuOp
):
"""
"""
Parameters
Parameters
...
@@ -438,13 +438,16 @@ def values_eq_approx_high_tol(a, b):
...
@@ -438,13 +438,16 @@ def values_eq_approx_high_tol(a, b):
@register_gpu_opt
()
@register_gpu_opt
()
@local_optimizer
([
Cum
sum
Op
])
@local_optimizer
([
CumOp
])
def
use_gpu_cumsum
(
node
):
def
use_gpu_cumsum
(
node
):
if
type
(
node
.
op
)
is
Cum
sum
Op
\
if
type
(
node
.
op
)
is
CumOp
\
and
node
.
inputs
[
0
]
.
dtype
==
'float32'
\
and
node
.
inputs
[
0
]
.
dtype
==
'float32'
\
and
node
.
inputs
[
0
]
.
owner
\
and
node
.
inputs
[
0
]
.
owner
\
and
isinstance
(
node
.
inputs
[
0
]
.
owner
.
op
,
HostFromGpu
):
and
isinstance
(
node
.
inputs
[
0
]
.
owner
.
op
,
HostFromGpu
):
if
node
.
op
.
mode
!=
'add'
:
return
None
axis
=
node
.
op
.
axis
axis
=
node
.
op
.
axis
x
=
node
.
inputs
[
0
]
x
=
node
.
inputs
[
0
]
...
...
theano/sandbox/cuda/tests/test_extra_ops.py
浏览文件 @
90dd93d0
...
@@ -7,7 +7,7 @@ import numpy as np
...
@@ -7,7 +7,7 @@ import numpy as np
from
six.moves
import
xrange
from
six.moves
import
xrange
from
theano
import
tensor
as
T
from
theano
import
tensor
as
T
import
theano
import
theano
from
theano.tensor.extra_ops
import
cumsum
,
Cum
sum
Op
from
theano.tensor.extra_ops
import
cumsum
,
CumOp
from
theano.tests
import
unittest_tools
as
utt
from
theano.tests
import
unittest_tools
as
utt
import
theano.sandbox.cuda
as
cuda_ndarray
import
theano.sandbox.cuda
as
cuda_ndarray
if
cuda_ndarray
.
cuda_available
:
if
cuda_ndarray
.
cuda_available
:
...
@@ -22,7 +22,7 @@ else:
...
@@ -22,7 +22,7 @@ else:
mode_with_gpu
=
theano
.
compile
.
mode
.
get_default_mode
()
.
including
(
'gpu'
)
mode_with_gpu
=
theano
.
compile
.
mode
.
get_default_mode
()
.
including
(
'gpu'
)
class
TestGpuCumsum
(
theano
.
tensor
.
tests
.
test_extra_ops
.
TestCum
sum
Op
):
class
TestGpuCumsum
(
theano
.
tensor
.
tests
.
test_extra_ops
.
TestCumOp
):
mode
=
mode_with_gpu
mode
=
mode_with_gpu
def
setUp
(
self
):
def
setUp
(
self
):
...
@@ -232,4 +232,4 @@ class TestGpuCumsum(theano.tensor.tests.test_extra_ops.TestCumsumOp):
...
@@ -232,4 +232,4 @@ class TestGpuCumsum(theano.tensor.tests.test_extra_ops.TestCumsumOp):
x
=
T
.
ftensor4
(
'x'
)
x
=
T
.
ftensor4
(
'x'
)
f
=
theano
.
function
([
x
],
cumsum
(
x
,
axis
=
1
),
mode
=
self
.
mode
)
f
=
theano
.
function
([
x
],
cumsum
(
x
,
axis
=
1
),
mode
=
self
.
mode
)
assert
[
n
for
n
in
f
.
maker
.
fgraph
.
toposort
()
assert
[
n
for
n
in
f
.
maker
.
fgraph
.
toposort
()
if
isinstance
(
n
.
op
,
Cum
sum
Op
)]
if
isinstance
(
n
.
op
,
CumOp
)]
theano/tensor/extra_ops.py
浏览文件 @
90dd93d0
...
@@ -242,13 +242,16 @@ def searchsorted(x, v, side='left', sorter=None):
...
@@ -242,13 +242,16 @@ def searchsorted(x, v, side='left', sorter=None):
return
SearchsortedOp
(
side
=
side
)(
x
,
v
,
sorter
)
return
SearchsortedOp
(
side
=
side
)(
x
,
v
,
sorter
)
class
Cum
sum
Op
(
theano
.
Op
):
class
CumOp
(
theano
.
Op
):
# See function cumsum for docstring
# See function cumsum
/cumprod
for docstring
__props__
=
(
"axis"
,)
__props__
=
(
"axis"
,
"mode"
)
def
__init__
(
self
,
axis
=
None
):
def
__init__
(
self
,
axis
=
None
,
mode
=
'add'
):
if
mode
not
in
(
'add'
,
'mul'
):
raise
ValueError
(
'
%
s: Unknown mode "
%
s"'
%
(
type
(
self
)
.
__name__
,
mode
))
self
.
axis
=
axis
self
.
axis
=
axis
self
.
mode
=
mode
def
make_node
(
self
,
x
):
def
make_node
(
self
,
x
):
x
=
basic
.
as_tensor_variable
(
x
)
x
=
basic
.
as_tensor_variable
(
x
)
...
@@ -264,20 +267,39 @@ class CumsumOp(theano.Op):
...
@@ -264,20 +267,39 @@ class CumsumOp(theano.Op):
def
perform
(
self
,
node
,
inputs
,
output_storage
):
def
perform
(
self
,
node
,
inputs
,
output_storage
):
x
=
inputs
[
0
]
x
=
inputs
[
0
]
z
=
output_storage
[
0
]
z
=
output_storage
[
0
]
z
[
0
]
=
np
.
cumsum
(
x
,
axis
=
self
.
axis
)
z
[
0
]
=
{
'add'
:
np
.
cumsum
,
'mul'
:
np
.
cumprod
}[
self
.
mode
]
(
x
,
axis
=
self
.
axis
)
def
grad
(
self
,
inputs
,
output_gradients
):
def
grad
(
self
,
inputs
,
output_gradients
):
[
gi
]
=
output_gradients
x
,
=
inputs
gi
,
=
output_gradients
if
self
.
axis
is
None
:
if
self
.
axis
is
None
:
return
[
cumsum
(
gi
[::
-
1
])[::
-
1
]
.
reshape
(
inputs
[
0
]
.
shape
)]
if
self
.
mode
==
'add'
:
return
[
cumsum
(
gi
[::
-
1
])[::
-
1
]
.
reshape
(
x
.
shape
)]
elif
self
.
mode
==
'mul'
:
fx
=
cumprod
(
x
,
axis
=
self
.
axis
)
return
[
cumsum
(
(
fx
*
gi
)[::
-
1
])[::
-
1
]
.
reshape
(
x
.
shape
)
/
x
]
else
:
raise
NotImplementedError
(
'
%
s: unknown gradient for mode "
%
s"'
%
(
type
(
self
)
.
__name__
,
self
.
mode
))
# We need to reverse the gradients along ``self.axis``,
# compute cumsum, then reverse again
reverse_slicing
=
[
slice
(
None
,
None
,
None
)]
*
gi
.
ndim
reverse_slicing
=
[
slice
(
None
,
None
,
None
)]
*
gi
.
ndim
reverse_slicing
[
self
.
axis
]
=
slice
(
None
,
None
,
-
1
)
reverse_slicing
[
self
.
axis
]
=
slice
(
None
,
None
,
-
1
)
reverse_slicing
=
tuple
(
reverse_slicing
)
reverse_slicing
=
tuple
(
reverse_slicing
)
return
[
cumsum
(
gi
[
reverse_slicing
],
self
.
axis
)[
reverse_slicing
]]
# We need to reverse the gradients along ``self.axis``,
# compute cumsum, then reverse again
if
self
.
mode
==
'add'
:
return
[
cumsum
(
gi
[
reverse_slicing
],
self
.
axis
)[
reverse_slicing
]]
elif
self
.
mode
==
'mul'
:
fx
=
cumprod
(
x
,
axis
=
self
.
axis
)
return
[
cumsum
(
(
fx
*
gi
)[
reverse_slicing
],
self
.
axis
)[
reverse_slicing
]
/
x
]
else
:
raise
NotImplementedError
(
'
%
s: unknown gradient for mode "
%
s"'
%
(
type
(
self
)
.
__name__
,
self
.
mode
))
def
infer_shape
(
self
,
node
,
shapes
):
def
infer_shape
(
self
,
node
,
shapes
):
if
self
.
axis
is
None
:
if
self
.
axis
is
None
:
...
@@ -290,6 +312,7 @@ class CumsumOp(theano.Op):
...
@@ -290,6 +312,7 @@ class CumsumOp(theano.Op):
z
,
=
onames
z
,
=
onames
axis
=
self
.
axis
axis
=
self
.
axis
fail
=
sub
[
'fail'
]
fail
=
sub
[
'fail'
]
func
=
dict
(
mul
=
'CumProd'
,
add
=
'CumSum'
)[
self
.
mode
]
if
self
.
axis
is
None
or
(
self
.
axis
==
0
and
node
.
inputs
[
0
]
.
ndim
==
1
):
if
self
.
axis
is
None
or
(
self
.
axis
==
0
and
node
.
inputs
[
0
]
.
ndim
==
1
):
code
=
"""
code
=
"""
...
@@ -303,13 +326,13 @@ class CumsumOp(theano.Op):
...
@@ -303,13 +326,13 @@ class CumsumOp(theano.Op):
if (!
%(z)
s)
if (!
%(z)
s)
%(fail)
s;
%(fail)
s;
{
{
PyObject * t = PyArray_
CumSum
(
PyObject * t = PyArray_
%(func)
s
(
%(x)
s, NPY_MAXDIMS,
%(x)
s, NPY_MAXDIMS,
PyArray_TYPE((PyArrayObject*) py_
%(x)
s),
%(z)
s);
PyArray_TYPE((PyArrayObject*) py_
%(x)
s),
%(z)
s);
if (!t){
if (!t){
%(fail)
s;
%(fail)
s;
}
}
// Because PyArray_
CumSum
returns a newly created reference on t.
// Because PyArray_
%(func)
s
returns a newly created reference on t.
Py_XDECREF(t);
Py_XDECREF(t);
}
}
"""
%
locals
()
"""
%
locals
()
...
@@ -325,13 +348,13 @@ class CumsumOp(theano.Op):
...
@@ -325,13 +348,13 @@ class CumsumOp(theano.Op):
%(fail)
s;
%(fail)
s;
{
{
PyObject * t = PyArray_
CumSum
(
PyObject * t = PyArray_
%(func)
s
(
%(x)
s,
%(axis)
s,
%(x)
s,
%(axis)
s,
PyArray_TYPE((PyArrayObject*) py_
%(x)
s),
%(z)
s);
PyArray_TYPE((PyArrayObject*) py_
%(x)
s),
%(z)
s);
if (!t){
if (!t){
%(fail)
s;
%(fail)
s;
}
}
// Because PyArray_
CumSum
returns a newly created reference on t.
// Because PyArray_
%(func)
s
returns a newly created reference on t.
Py_XDECREF(t);
Py_XDECREF(t);
}
}
"""
%
locals
()
"""
%
locals
()
...
@@ -339,10 +362,10 @@ class CumsumOp(theano.Op):
...
@@ -339,10 +362,10 @@ class CumsumOp(theano.Op):
return
code
return
code
def
c_code_cache_version
(
self
):
def
c_code_cache_version
(
self
):
return
(
6
,)
return
(
7
,)
def
__str__
(
self
):
def
__str__
(
self
):
return
"
%
s{
%
s
}"
%
(
self
.
__class__
.
__name__
,
self
.
axis
)
return
"
%
s{
%
s
,
%
s}"
%
(
self
.
__class__
.
__name__
,
self
.
axis
,
self
.
mode
)
def
cumsum
(
x
,
axis
=
None
):
def
cumsum
(
x
,
axis
=
None
):
...
@@ -362,112 +385,7 @@ def cumsum(x, axis=None):
...
@@ -362,112 +385,7 @@ def cumsum(x, axis=None):
.. versionadded:: 0.7
.. versionadded:: 0.7
"""
"""
return
CumsumOp
(
axis
=
axis
)(
x
)
return
CumOp
(
axis
=
axis
,
mode
=
'add'
)(
x
)
class
CumprodOp
(
theano
.
Op
):
# See function cumprod for docstring
__props__
=
(
"axis"
,)
def
__init__
(
self
,
axis
=
None
):
self
.
axis
=
axis
def
make_node
(
self
,
x
):
x
=
basic
.
as_tensor_variable
(
x
)
out_type
=
x
.
type
()
if
self
.
axis
is
None
:
out_type
=
theano
.
tensor
.
vector
(
dtype
=
x
.
dtype
)
# Flatten
elif
self
.
axis
>=
x
.
ndim
or
self
.
axis
<
-
x
.
ndim
:
raise
ValueError
(
'axis(={0}) out of bounds'
.
format
(
self
.
axis
))
return
theano
.
Apply
(
self
,
[
x
],
[
out_type
])
def
perform
(
self
,
node
,
inputs
,
output_storage
):
x
=
inputs
[
0
]
z
=
output_storage
[
0
]
z
[
0
]
=
np
.
cumprod
(
x
,
axis
=
self
.
axis
)
def
grad
(
self
,
inputs
,
output_gradients
):
x
,
=
inputs
gi
,
=
output_gradients
fx
=
cumprod
(
x
,
axis
=
self
.
axis
)
if
self
.
axis
is
None
:
return
[
cumsum
((
fx
*
gi
)[::
-
1
])[::
-
1
]
.
reshape
(
inputs
[
0
]
.
shape
)
/
x
]
# We need to reverse the gradients along ``self.axis``,
# compute cumsum, then reverse again
reverse_slicing
=
[
slice
(
None
,
None
,
None
)]
*
gi
.
ndim
reverse_slicing
[
self
.
axis
]
=
slice
(
None
,
None
,
-
1
)
reverse_slicing
=
tuple
(
reverse_slicing
)
return
[
cumsum
((
fx
*
gi
)[
reverse_slicing
],
self
.
axis
)[
reverse_slicing
]
/
x
]
def
infer_shape
(
self
,
node
,
shapes
):
if
self
.
axis
is
None
:
return
[(
tensor
.
prod
(
shapes
[
0
]),)]
# Flatten
return
shapes
def
c_code
(
self
,
node
,
name
,
inames
,
onames
,
sub
):
x
,
=
inames
z
,
=
onames
axis
=
self
.
axis
fail
=
sub
[
'fail'
]
if
self
.
axis
is
None
or
(
self
.
axis
==
0
and
node
.
inputs
[
0
]
.
ndim
==
1
):
code
=
"""
npy_intp shape[1] = { PyArray_SIZE(
%(x)
s) };
if(!(
%(z)
s && PyArray_DIMS(
%(z)
s)[0] == shape[0]))
{
Py_XDECREF(
%(z)
s);
%(z)
s = (PyArrayObject*) PyArray_SimpleNew(1, shape, PyArray_TYPE((PyArrayObject*) py_
%(x)
s));
}
if (!
%(z)
s)
%(fail)
s;
{
PyObject * t = PyArray_CumProd(
%(x)
s, NPY_MAXDIMS,
PyArray_TYPE((PyArrayObject*) py_
%(x)
s),
%(z)
s);
if (!t){
%(fail)
s;
}
// Because PyArray_CumSum returns a newly created reference on t.
Py_XDECREF(t);
}
"""
%
locals
()
else
:
code
=
"""
if(!(
%(z)
s && PyArray_CompareLists(PyArray_DIMS(
%(z)
s), PyArray_DIMS(
%(x)
s), PyArray_NDIM(
%(x)
s)) ))
{
Py_XDECREF(
%(z)
s);
%(z)
s = (PyArrayObject*) PyArray_SimpleNew(PyArray_NDIM(
%(x)
s), PyArray_DIMS(
%(x)
s), PyArray_TYPE((PyArrayObject*) py_
%(x)
s));
}
if (!
%(z)
s)
%(fail)
s;
{
PyObject * t = PyArray_CumProd(
%(x)
s,
%(axis)
s,
PyArray_TYPE((PyArrayObject*) py_
%(x)
s),
%(z)
s);
if (!t){
%(fail)
s;
}
// Because PyArray_CumSum returns a newly created reference on t.
Py_XDECREF(t);
}
"""
%
locals
()
return
code
def
c_code_cache_version
(
self
):
return
(
4
,)
def
__str__
(
self
):
return
"
%
s{
%
s}"
%
(
self
.
__class__
.
__name__
,
self
.
axis
)
def
cumprod
(
x
,
axis
=
None
):
def
cumprod
(
x
,
axis
=
None
):
...
@@ -488,7 +406,27 @@ def cumprod(x, axis=None):
...
@@ -488,7 +406,27 @@ def cumprod(x, axis=None):
.. versionadded:: 0.7
.. versionadded:: 0.7
"""
"""
return
CumprodOp
(
axis
=
axis
)(
x
)
return
CumOp
(
axis
=
axis
,
mode
=
'mul'
)(
x
)
# CumsumOp and CumprodOp are for compatibility with old version,
# just in case unpickling a theano function with old Ops.
class
CumsumOp
(
theano
.
Op
):
__props__
=
(
"axis"
,)
def
__new__
(
typ
,
*
args
,
**
kwargs
):
obj
=
object
.
__new__
(
CumOp
,
*
args
,
**
kwargs
)
obj
.
mode
=
'add'
return
obj
class
CumprodOp
(
theano
.
Op
):
__props__
=
(
"axis"
,)
def
__new__
(
typ
,
*
args
,
**
kwargs
):
obj
=
object
.
__new__
(
CumOp
,
*
args
,
**
kwargs
)
obj
.
mode
=
'mul'
return
obj
class
DiffOp
(
theano
.
Op
):
class
DiffOp
(
theano
.
Op
):
...
...
theano/tensor/tests/test_extra_ops.py
浏览文件 @
90dd93d0
from
__future__
import
absolute_import
,
print_function
,
division
from
__future__
import
absolute_import
,
print_function
,
division
from
functools
import
partial
import
numpy
as
np
import
numpy
as
np
import
numpy
import
numpy
...
@@ -7,7 +8,7 @@ import theano
...
@@ -7,7 +8,7 @@ import theano
from
theano.tests
import
unittest_tools
as
utt
from
theano.tests
import
unittest_tools
as
utt
from
theano.tensor.extra_ops
import
(
SearchsortedOp
,
searchsorted
,
from
theano.tensor.extra_ops
import
(
SearchsortedOp
,
searchsorted
,
Cum
sumOp
,
cumsum
,
CumprodOp
,
cumprod
,
Cum
Op
,
cumsum
,
cumprod
,
CpuContiguous
,
cpu_contiguous
,
CpuContiguous
,
cpu_contiguous
,
bincount
,
DiffOp
,
diff
,
squeeze
,
compress
,
bincount
,
DiffOp
,
diff
,
squeeze
,
compress
,
RepeatOp
,
repeat
,
Bartlett
,
bartlett
,
RepeatOp
,
repeat
,
Bartlett
,
bartlett
,
...
@@ -121,74 +122,33 @@ class TestSearchsortedOp(utt.InferShapeTester):
...
@@ -121,74 +122,33 @@ class TestSearchsortedOp(utt.InferShapeTester):
utt
.
verify_grad
(
self
.
op
,
[
self
.
a
[
self
.
idx_sorted
],
self
.
b
])
utt
.
verify_grad
(
self
.
op
,
[
self
.
a
[
self
.
idx_sorted
],
self
.
b
])
class
TestCum
sum
Op
(
utt
.
InferShapeTester
):
class
TestCumOp
(
utt
.
InferShapeTester
):
def
setUp
(
self
):
def
setUp
(
self
):
super
(
TestCum
sum
Op
,
self
)
.
setUp
()
super
(
TestCumOp
,
self
)
.
setUp
()
self
.
op_class
=
Cum
sum
Op
self
.
op_class
=
CumOp
self
.
op
=
Cum
sum
Op
()
self
.
op
=
CumOp
()
def
test_cum
sumO
p
(
self
):
def
test_cum
_o
p
(
self
):
x
=
T
.
tensor3
(
'x'
)
x
=
T
.
tensor3
(
'x'
)
a
=
np
.
random
.
random
((
3
,
5
,
2
))
.
astype
(
config
.
floatX
)
a
=
np
.
random
.
random
((
3
,
5
,
2
))
.
astype
(
config
.
floatX
)
# Test axis out of bounds
# Test axis out of bounds
self
.
assertRaises
(
ValueError
,
cumsum
,
x
,
axis
=
3
)
self
.
assertRaises
(
ValueError
,
cumsum
,
x
,
axis
=
3
)
self
.
assertRaises
(
ValueError
,
cumsum
,
x
,
axis
=-
4
)
self
.
assertRaises
(
ValueError
,
cumsum
,
x
,
axis
=-
4
)
f
=
theano
.
function
([
x
],
cumsum
(
x
))
assert
np
.
allclose
(
np
.
cumsum
(
a
),
f
(
a
))
# Test axis=None
for
axis
in
range
(
-
len
(
a
.
shape
),
len
(
a
.
shape
)):
f
=
theano
.
function
([
x
],
cumsum
(
x
,
axis
=
axis
))
assert
np
.
allclose
(
np
.
cumsum
(
a
,
axis
=
axis
),
f
(
a
))
def
test_infer_shape
(
self
):
x
=
T
.
tensor3
(
'x'
)
a
=
np
.
random
.
random
((
3
,
5
,
2
))
.
astype
(
config
.
floatX
)
# Test axis=None
self
.
_compile_and_check
([
x
],
[
self
.
op
(
x
)],
[
a
],
self
.
op_class
)
for
axis
in
range
(
-
len
(
a
.
shape
),
len
(
a
.
shape
)):
self
.
_compile_and_check
([
x
],
[
cumsum
(
x
,
axis
=
axis
)],
[
a
],
self
.
op_class
)
def
test_grad
(
self
):
a
=
np
.
random
.
random
((
3
,
5
,
2
))
.
astype
(
config
.
floatX
)
utt
.
verify_grad
(
self
.
op
,
[
a
])
# Test axis=None
for
axis
in
range
(
-
len
(
a
.
shape
),
len
(
a
.
shape
)):
utt
.
verify_grad
(
self
.
op_class
(
axis
=
axis
),
[
a
],
eps
=
4e-4
)
class
TestCumprodOp
(
utt
.
InferShapeTester
):
def
setUp
(
self
):
super
(
TestCumprodOp
,
self
)
.
setUp
()
self
.
op_class
=
CumprodOp
self
.
op
=
CumprodOp
()
def
test_CumprodOp
(
self
):
x
=
T
.
tensor3
(
'x'
)
a
=
np
.
random
.
random
((
3
,
5
,
2
))
.
astype
(
config
.
floatX
)
# Test axis out of bounds
self
.
assertRaises
(
ValueError
,
cumprod
,
x
,
axis
=
3
)
self
.
assertRaises
(
ValueError
,
cumprod
,
x
,
axis
=
3
)
self
.
assertRaises
(
ValueError
,
cumprod
,
x
,
axis
=-
4
)
self
.
assertRaises
(
ValueError
,
cumprod
,
x
,
axis
=-
4
)
f
=
theano
.
function
([
x
],
cumprod
(
x
))
f
=
theano
.
function
([
x
],
[
cumsum
(
x
),
cumprod
(
x
)])
assert
np
.
allclose
(
np
.
cumprod
(
a
),
f
(
a
))
# Test axis=None
s
,
p
=
f
(
a
)
assert
np
.
allclose
(
np
.
cumsum
(
a
),
s
)
# Test axis=None
assert
np
.
allclose
(
np
.
cumprod
(
a
),
p
)
# Test axis=None
for
axis
in
range
(
-
len
(
a
.
shape
),
len
(
a
.
shape
)):
for
axis
in
range
(
-
len
(
a
.
shape
),
len
(
a
.
shape
)):
f
=
theano
.
function
([
x
],
cumprod
(
x
,
axis
=
axis
))
f
=
theano
.
function
([
x
],
[
cumsum
(
x
,
axis
=
axis
),
cumprod
(
x
,
axis
=
axis
)])
assert
np
.
allclose
(
np
.
cumprod
(
a
,
axis
=
axis
),
f
(
a
))
s
,
p
=
f
(
a
)
assert
np
.
allclose
(
np
.
cumsum
(
a
,
axis
=
axis
),
s
)
assert
np
.
allclose
(
np
.
cumprod
(
a
,
axis
=
axis
),
p
)
def
test_infer_shape
(
self
):
def
test_infer_shape
(
self
):
x
=
T
.
tensor3
(
'x'
)
x
=
T
.
tensor3
(
'x'
)
...
@@ -202,17 +162,19 @@ class TestCumprodOp(utt.InferShapeTester):
...
@@ -202,17 +162,19 @@ class TestCumprodOp(utt.InferShapeTester):
for
axis
in
range
(
-
len
(
a
.
shape
),
len
(
a
.
shape
)):
for
axis
in
range
(
-
len
(
a
.
shape
),
len
(
a
.
shape
)):
self
.
_compile_and_check
([
x
],
self
.
_compile_and_check
([
x
],
[
cum
prod
(
x
,
axis
=
axis
)],
[
cum
sum
(
x
,
axis
=
axis
)],
[
a
],
[
a
],
self
.
op_class
)
self
.
op_class
)
def
test_grad
(
self
):
def
test_grad
(
self
):
a
=
np
.
random
.
random
((
3
,
5
,
2
))
.
astype
(
config
.
floatX
)
a
=
np
.
random
.
random
((
3
,
5
,
2
))
.
astype
(
config
.
floatX
)
utt
.
verify_grad
(
self
.
op
,
[
a
])
# Test axis=None
utt
.
verify_grad
(
self
.
op_class
(
mode
=
'add'
),
[
a
])
# Test axis=None
utt
.
verify_grad
(
self
.
op_class
(
mode
=
'mul'
),
[
a
])
# Test axis=None
for
axis
in
range
(
-
len
(
a
.
shape
),
len
(
a
.
shape
)):
for
axis
in
range
(
-
len
(
a
.
shape
),
len
(
a
.
shape
)):
utt
.
verify_grad
(
self
.
op_class
(
axis
=
axis
),
[
a
])
utt
.
verify_grad
(
self
.
op_class
(
axis
=
axis
,
mode
=
'add'
),
[
a
],
eps
=
4e-4
)
utt
.
verify_grad
(
self
.
op_class
(
axis
=
axis
,
mode
=
'mul'
),
[
a
],
eps
=
4e-4
)
class
TestBinCount
(
utt
.
InferShapeTester
):
class
TestBinCount
(
utt
.
InferShapeTester
):
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论