Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
P
pytensor
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
testgroup
pytensor
Commits
d676edaf
提交
d676edaf
authored
3月 15, 2008
作者:
Olivier Breuleux
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
added base_tensor, some reorganizing, recovered some C support
上级
26d56c68
显示空白字符变更
内嵌
并排
正在显示
7 个修改的文件
包含
287 行增加
和
438 行删除
+287
-438
_test_tensor.py
_test_tensor.py
+8
-1
base_tensor.py
base_tensor.py
+171
-0
elemwise.py
elemwise.py
+2
-20
cc.py
gof/cc.py
+4
-0
gradient.py
gradient.py
+2
-0
tensor.py
tensor.py
+70
-209
tensor_ops.py
tensor_ops.py
+30
-208
没有找到文件。
_test_tensor.py
浏览文件 @
d676edaf
...
@@ -5,6 +5,8 @@ import unittest
...
@@ -5,6 +5,8 @@ import unittest
from
copy
import
copy
from
copy
import
copy
from
compile
import
Function
from
compile
import
Function
import
gradient
import
gradient
import
gof
#TODO: consider moving this function / functionality to gradient.py
#TODO: consider moving this function / functionality to gradient.py
# rationale: it's tricky, and necessary everytime you want to verify
# rationale: it's tricky, and necessary everytime you want to verify
...
@@ -57,7 +59,7 @@ class T_tensor(unittest.TestCase):
...
@@ -57,7 +59,7 @@ class T_tensor(unittest.TestCase):
def
test0_int
(
self
):
# allocate from a scalar float
def
test0_int
(
self
):
# allocate from a scalar float
t
=
tensor
(
1
)
t
=
tensor
(
1
)
self
.
failUnless
(
isinstance
(
t
,
Tensor
))
self
.
failUnless
(
isinstance
(
t
,
Tensor
))
self
.
failUnless
(
t
.
dtype
==
'int64'
)
self
.
failUnless
(
t
.
dtype
==
'int64'
or
t
.
dtype
==
'int32'
)
def
test1
(
self
):
# allocate from a vector of ints, not broadcastable
def
test1
(
self
):
# allocate from a vector of ints, not broadcastable
t
=
tensor
(
numpy
.
ones
(
5
,
dtype
=
'int32'
))
t
=
tensor
(
numpy
.
ones
(
5
,
dtype
=
'int32'
))
self
.
failUnless
(
isinstance
(
t
,
Tensor
))
self
.
failUnless
(
isinstance
(
t
,
Tensor
))
...
@@ -141,6 +143,11 @@ def check_eq2(self, inputs, output, args_in, arg_out):
...
@@ -141,6 +143,11 @@ def check_eq2(self, inputs, output, args_in, arg_out):
val
=
fn
(
*
args_in
)
val
=
fn
(
*
args_in
)
self
.
failUnless
(
numpy
.
all
(
val
==
arg_out
),
(
val
,
arg_out
))
self
.
failUnless
(
numpy
.
all
(
val
==
arg_out
),
(
val
,
arg_out
))
def
check_eq2
(
self
,
inputs
,
output
,
args_in
,
arg_out
):
fn
=
Function
(
inputs
,
[
output
],
linker_cls
=
gof
.
CLinker
)
val
=
fn
(
*
args_in
)
self
.
failUnless
(
numpy
.
all
(
val
==
arg_out
),
(
val
,
arg_out
))
class
T_abs
(
unittest
.
TestCase
):
class
T_abs
(
unittest
.
TestCase
):
def
test_impl
(
self
):
def
test_impl
(
self
):
...
...
base_tensor.py
0 → 100644
浏览文件 @
d676edaf
from
gof
import
ResultBase
import
numpy
from
copy
import
copy
###########################
# BaseTensor Class
###########################
class
BaseTensor
(
ResultBase
):
"""ResultBase to store numpy.ndarray or equivalent via .data
Attributes:
_dtype - numpy dtype string such as 'int64' or 'float64' (among others)
_broadcastable - tuple of ints in (0,1) saying which dimensions of this
tensor are guaranteed to be 1, and up for broadcasting
Properties:
dtype - read-only access to _dtype, which should not be changed
broadcastable - read-only access to _broadcastable, which should not be changed
This class does not implement python operators and has no dependencies
on the Ops that use it.
"""
def
__init__
(
self
,
dtype
,
broadcastable
,
role
=
None
,
name
=
None
):
"""Initialize a Tensor"""
# data is not given here. This may seem a bit strange, but when data was
# an argument, it made sense to use *either* the given dtype,
# broadcastable, or override them from the fields of data. This makes
# the function ugly, especially because it isn't obvious how to set
# broadcastable from data.
#
# The only clean option I could think of, when passing a data arg was to
# require the broadcastable field to be given. Since broadcastable is
# the argument that is awkward to construct, I decided to put all this
# into the tensor(data,...) function below, which is like a second
# constructor that works with an ndarray.
ResultBase
.
__init__
(
self
,
role
=
role
,
name
=
name
)
self
.
_dtype
=
str
(
dtype
)
self
.
_broadcastable
=
tuple
(
broadcastable
)
######################
# ResultBase interface
######################
#
# filter
#
def
filter
(
self
,
arr
):
if
not
isinstance
(
arr
,
numpy
.
ndarray
):
arr
=
numpy
.
asarray
(
arr
,
dtype
=
self
.
dtype
)
if
len
(
self
.
broadcastable
)
!=
len
(
arr
.
shape
):
raise
ValueError
(
BaseTensor
.
filter
.
E_rank
)
for
b
,
s
in
zip
(
self
.
broadcastable
,
arr
.
shape
):
if
b
and
(
s
!=
1
):
raise
ValueError
(
BaseTensor
.
filter
.
E_shape
)
return
arr
# these strings are here so that tests can use them
filter
.
E_rank
=
'wrong rank'
filter
.
E_shape
=
'non-unit size on broadcastable dimension'
#
# type information
#
def
dtype_specs
(
self
):
"""Return python - C type correspondance tuple for self.data
Return a tuple (python type, c type, numpy typenum) that corresponds to
self.dtype. It is for use in C code generation.
"""
#TODO: add more type correspondances for e.g. int32, int64, float32,
#complex64, etc.
return
{
'float64'
:
(
float
,
'double'
,
'NPY_DOUBLE'
)}[
self
.
dtype
]
#
# C codegen stubs
#
def
c_declare
(
self
):
return
"""
PyArrayObject*
%%(name)
s;
int type_num_
%%(name)
s;
typedef
%(dtype)
s dtype_
%%(name)
s;
"""
%
dict
(
dtype
=
self
.
dtype_specs
()[
1
])
def
c_init
(
self
):
return
"""
%%(name)
s = NULL;
type_num_
%%(name)
s =
%(type_num)
s;
"""
%
dict
(
type_num
=
self
.
dtype_specs
()[
2
])
def
c_extract
(
self
):
return
"""
%%(name)
s = NULL;
type_num_
%%(name)
s =
%(type_num)
s;
if (py_
%%(name)
s == Py_None) {
// We can either fail here or set
%%(name)
s to NULL and rely on Ops using
// tensors to handle the NULL case, but if they fail to do so they'll end up
// with nasty segfaults, so this is public service.
PyErr_SetString(PyExc_ValueError, "expected an ndarray, not None");
%%(fail)
s
//
%%(name)
s = NULL;
}
else if (!PyArray_Check(py_
%%(name)
s)) {
PyErr_SetString(PyExc_ValueError, "expected an ndarray");
%%(fail)
s
}
else if (((PyArrayObject*)py_
%%(name)
s)->descr->type_num !=
%(type_num)
s) {
PyErr_SetString(PyExc_ValueError, "expected
%(type_num)
s");
%%(fail)
s
}
else {
%%(name)
s = (PyArrayObject*)(py_
%%(name)
s);
Py_XINCREF(
%%(name)
s);
}
"""
%
dict
(
type_num
=
self
.
dtype_specs
()[
2
])
def
c_cleanup
(
self
):
return
"""
if (
%(name)
s) {
Py_XDECREF(
%(name)
s);
}
"""
def
c_sync
(
self
):
return
"""
if (!
%(name)
s) {
Py_XDECREF(py_
%(name)
s);
py_
%(name)
s = Py_None;
}
else if ((void*)py_
%(name)
s != (void*)
%(name)
s) {
Py_XDECREF(py_
%(name)
s);
py_
%(name)
s = (PyObject*)
%(name)
s;
Py_XINCREF(py_
%(name)
s);
}
"""
def
c_headers
(
self
):
return
[]
def
c_libraries
(
self
):
return
[]
############################
# Tensor specific attributes
############################
dtype
=
property
(
lambda
self
:
self
.
_dtype
)
broadcastable
=
property
(
lambda
self
:
self
.
_broadcastable
)
############################
# Cloning facilities
############################
def
__copy__
(
self
):
return
self
.
clone
(
True
)
def
clone
(
self
,
transfer_data
=
False
):
"""
Returns a copy of this Tensor. If there is data stored inside it, it is also copied.
"""
cpy
=
self
.
__class__
(
self
.
dtype
,
self
.
broadcastable
,
None
,
self
.
name
)
if
transfer_data
:
cpy
.
data
=
copy
(
self
.
data
)
return
cpy
elemwise.py
浏览文件 @
d676edaf
from
copy
import
copy
from
copy
import
copy
from
gof
import
Op
from
gof
import
Op
,
Destroyer
from
gof.utils
import
AbstractFunctionError
from
gof.utils
import
AbstractFunctionError
from
tensor
import
_Op
class
Elemwise
(
Op
):
class
Elemwise
(
_Op
):
def
var_desc
(
self
):
def
var_desc
(
self
):
raise
AbstractFunctionError
()
raise
AbstractFunctionError
()
...
@@ -20,22 +18,6 @@ class Elemwise(_Op):
...
@@ -20,22 +18,6 @@ class Elemwise(_Op):
return
[[
i
[
0
]
for
i
in
idesc
if
i
[
1
]],
return
[[
i
[
0
]
for
i
in
idesc
if
i
[
1
]],
[
o
[
0
]
for
o
in
odesc
if
o
[
1
]]]
[
o
[
0
]
for
o
in
odesc
if
o
[
1
]]]
def
propagate_broadcastable
(
self
,
*
inputs
):
idesc
,
odesc
=
self
.
var_desc
()
nonloop_o
=
[
o
[
0
]
for
o
in
odesc
if
not
o
[
1
]]
if
nonloop_o
:
raise
Exception
(
"Cannot infer broadcastable for non-loop variable(s)
%
s"
%
nonloop_o
)
all_bcast
=
[
broadcastable
for
broadcastable
,
i
in
zip
(
inputs
,
idesc
)
if
i
[
1
]]
if
reduce
(
lambda
x
,
y
:
x
is
not
False
and
x
==
y
and
y
,
[
len
(
x
)
for
x
in
all_bcast
])
is
False
:
raise
TypeError
(
"Inputs that are loop variables do not all have the same number of dimensions."
)
ret
=
[]
for
arr
in
zip
(
*
all_bcast
):
if
0
in
arr
:
ret
.
append
(
0
)
else
:
ret
.
append
(
1
)
return
[
ret
]
*
self
.
nout
def
c_code_init
(
self
):
def
c_code_init
(
self
):
raise
AbstractFunctionError
()
raise
AbstractFunctionError
()
...
...
gof/cc.py
浏览文件 @
d676edaf
...
@@ -234,7 +234,11 @@ class CLinker(Linker):
...
@@ -234,7 +234,11 @@ class CLinker(Linker):
env
=
self
.
env
env
=
self
.
env
self
.
inputs
=
env
.
inputs
self
.
inputs
=
env
.
inputs
if
len
(
set
(
self
.
inputs
))
!=
len
(
self
.
inputs
):
raise
Exception
(
"CLinker doesn't support duplicate inputs."
)
self
.
outputs
=
env
.
outputs
self
.
outputs
=
env
.
outputs
if
len
(
set
(
self
.
outputs
))
!=
len
(
self
.
outputs
):
raise
Exception
(
"CLinker doesn't support duplicate outputs."
)
try
:
self
.
results
=
list
(
env
.
results
())
try
:
self
.
results
=
list
(
env
.
results
())
except
AttributeError
:
self
.
results
=
self
.
inputs
+
self
.
outputs
except
AttributeError
:
self
.
results
=
self
.
inputs
+
self
.
outputs
...
...
gradient.py
浏览文件 @
d676edaf
import
gof
,
gof
.
result
import
gof
,
gof
.
result
import
numpy
#for numeric_grad
import
numpy
#for numeric_grad
from
gof.python25
import
all
_msg_retNone
=
'op.grad(...) returned None, consider returning [None]'
_msg_retNone
=
'op.grad(...) returned None, consider returning [None]'
_msg_badlen
=
'op.grad(...) returned wrong number of gradients'
_msg_badlen
=
'op.grad(...) returned wrong number of gradients'
...
...
tensor.py
浏览文件 @
d676edaf
...
@@ -3,162 +3,21 @@
...
@@ -3,162 +3,21 @@
import
numpy
import
numpy
from
copy
import
copy
from
copy
import
copy
import
inspect
import
inspect
from
gof
import
ResultBase
,
Op
,
utils
,
Destroyer
,
Viewer
from
gof
import
ResultBase
,
Op
,
utils
,
Destroyer
,
Viewer
,
AbstractFunctionError
###########################
from
base_tensor
import
BaseTensor
# Tensor Class
from
elemwise
import
Elemwise
###########################
class
Tensor
(
ResultBase
):
"""ResultBase to store numpy.ndarray or equivalent via .data
Attributes:
class
Tensor
(
BaseTensor
):
_dtype - numpy dtype string such as 'int64' or 'float64' (among others)
"""
_broadcastable - tuple of ints in (0,1) saying which dimensions of this
This subclass of BaseTensor provides operator overloading using implementations
tensor are guaranteed to be 1, and up for broadcasting
of Tensor operations contained in this file.
Properties:
dtype - read-only access to _dtype, which should not be changed
broadcastable - read-only access to _broadcastable, which should not be changed
Operators:
Operators:
- most numeric operators are overloaded to return Ops that *would* perform
- most numeric operators are overloaded to return Ops that *would* perform
the corresponding calculation
the corresponding calculation
"""
def
__init__
(
self
,
dtype
,
broadcastable
,
role
=
None
,
name
=
None
):
"""Initialize a Tensor"""
# data is not given here. This may seem a bit strange, but when data was
# an argument, it made sense to use *either* the given dtype,
# broadcastable, or override them from the fields of data. This makes
# the function ugly, especially because it isn't obvious how to set
# broadcastable from data.
#
# The only clean option I could think of, when passing a data arg was to
# require the broadcastable field to be given. Since broadcastable is
# the argument that is awkward to construct, I decided to put all this
# into the tensor(data,...) function below, which is like a second
# constructor that works with an ndarray.
ResultBase
.
__init__
(
self
,
role
=
role
,
name
=
name
)
self
.
_dtype
=
str
(
dtype
)
self
.
_broadcastable
=
tuple
(
broadcastable
)
######################
# ResultBase interface
######################
#
# filter
#
def
filter
(
self
,
arr
):
if
not
isinstance
(
arr
,
numpy
.
ndarray
):
arr
=
numpy
.
asarray
(
arr
,
dtype
=
self
.
dtype
)
if
len
(
self
.
broadcastable
)
!=
len
(
arr
.
shape
):
raise
ValueError
(
Tensor
.
filter
.
E_rank
)
for
b
,
s
in
zip
(
self
.
broadcastable
,
arr
.
shape
):
if
b
and
(
s
!=
1
):
raise
ValueError
(
Tensor
.
filter
.
E_shape
)
return
arr
# these strings are here so that tests can use them
filter
.
E_rank
=
'wrong rank'
filter
.
E_shape
=
'non-unit size on broadcastable dimension'
#
# type information : Olivier what does this mean?
#
def
dtype_specs
(
self
):
"""Return python - C type correspondance tuple for self.data
Return a tuple (python type, c type, numpy typenum) that corresponds to
self.dtype. It is for use in C code generation.
"""
#TODO: add more type correspondances for e.g. int32, int64, float32,
#complex64, etc.
return
{
'float64'
:
(
float
,
'double'
,
'NPY_DOUBLE'
)}[
self
.
dtype
]
#
# C codegen stubs
#
def
c_declare
(
self
):
return
"""
PyArrayObject*
%%(name)
s;
int type_num_
%%(name)
s;
typedef
%(dtype)
s dtype_
%%(name)
s;
"""
%
dict
(
dtype
=
self
.
dtype_specs
()[
1
])
def
c_init
(
self
):
return
"""
%%(name)
s = NULL;
type_num_
%%(name)
s =
%(type_num)
s;
"""
%
dict
(
type_num
=
self
.
dtype_specs
()[
2
])
def
c_extract
(
self
):
return
"""
%%(name)
s = NULL;
type_num_
%%(name)
s =
%(type_num)
s;
if (py_
%%(name)
s == Py_None) {
%%(name)
s = NULL;
}
else if (!PyArray_Check(py_
%%(name)
s)) {
PyErr_SetString(PyExc_ValueError, "expected an ndarray");
%%(fail)
s
}
else if (((PyArrayObject*)py_
%%(name)
s)->descr->type_num !=
%(type_num)
s) {
PyErr_SetString(PyExc_ValueError, "expected
%(type_num)
s");
%%(fail)
s
}
else {
%%(name)
s = (PyArrayObject*)(py_
%%(name)
s);
Py_XINCREF(
%%(name)
s);
}
"""
%
dict
(
type_num
=
self
.
dtype_specs
()[
2
])
def
c_cleanup
(
self
):
return
"""
if (
%(name)
s) {
Py_XDECREF(
%(name)
s);
}
"""
def
c_sync
(
self
):
return
"""
if (!
%(name)
s) {
Py_XDECREF(py_
%(name)
s);
py_
%(name)
s = Py_None;
}
else if ((void*)py_
%(name)
s != (void*)
%(name)
s) {
Py_XDECREF(py_
%(name)
s);
py_
%(name)
s = (PyObject*)
%(name)
s;
Py_XINCREF(py_
%(name)
s);
}
"""
def
c_headers
(
self
):
return
[]
def
c_libraries
(
self
):
return
[]
############################
# Tensor specific attributes
#
############################
dtype
=
property
(
lambda
self
:
self
.
_dtype
)
broadcastable
=
property
(
lambda
self
:
self
.
_broadcastable
)
# STDLIB
def
__copy__
(
self
):
"""
Returns a copy of this Tensor. If there is data stored inside it, it is also copied.
"""
"""
cpy
=
self
.
__class__
(
self
.
dtype
,
self
.
broadcastable
,
None
,
self
.
name
)
cpy
.
data
=
copy
(
self
.
data
)
return
cpy
#UNARY
#UNARY
def
__abs__
(
self
):
return
Abs
(
self
)
.
out
def
__abs__
(
self
):
return
Abs
(
self
)
.
out
...
@@ -241,6 +100,20 @@ def _scalar_switch(normal_f, scalar_f, scalar_f_reverse = None):
...
@@ -241,6 +100,20 @@ def _scalar_switch(normal_f, scalar_f, scalar_f_reverse = None):
return
normal_f
(
x
,
y
)
return
normal_f
(
x
,
y
)
return
f
return
f
def
_assert_same_shapes
(
x
,
*
rest
):
"""Ensure that all inputs to the function impl have the same size (foils numpy's broadcasting)"""
shape
=
x
.
shape
for
other
in
rest
:
if
other
.
shape
!=
shape
:
raise
_assert_same_shapes
.
E_shape
_assert_same_shapes
.
E_shape
=
ValueError
(
"The dimensions of the inputs do not match."
)
def
_assert_tensor_scalar
(
x
,
a
):
"""ensure that the second input is a scalar"""
if
numpy
.
product
(
a
.
shape
)
!=
1
:
raise
ValueError
(
"The second argument must be a scalar."
)
class
_Op
(
Op
):
class
_Op
(
Op
):
"""A convenient base for the ops in this file"""
"""A convenient base for the ops in this file"""
nin
=
-
1
nin
=
-
1
...
@@ -288,7 +161,12 @@ class _Op(Op):
...
@@ -288,7 +161,12 @@ class _Op(Op):
raise
AbstractFunctionError
()
raise
AbstractFunctionError
()
def
perform
(
self
):
def
perform
(
self
):
self
.
outputs
[
0
]
.
data
=
self
.
impl
(
*
[
input
.
data
for
input
in
self
.
inputs
])
res
=
self
.
impl
(
*
[
input
.
data
for
input
in
self
.
inputs
])
if
self
.
nout
==
1
:
self
.
outputs
[
0
]
.
data
=
res
else
:
for
output
,
value
in
zip
(
self
.
outputs
,
res
):
output
.
data
=
value
def
c_var_names
(
self
):
def
c_var_names
(
self
):
(
self
,
inames
,
onames
),
_1
,
_2
,
_3
=
inspect
.
getargspec
(
self
.
c_impl
)
(
self
,
inames
,
onames
),
_1
,
_2
,
_3
=
inspect
.
getargspec
(
self
.
c_impl
)
...
@@ -308,20 +186,8 @@ class _Unary:
...
@@ -308,20 +186,8 @@ class _Unary:
class
_Binary
:
class
_Binary
:
nin
=
2
nin
=
2
def
_assert_same_shapes
(
x
,
*
rest
):
"""Ensure that all inputs to the function impl have the same size (foils numpy's broadcasting)"""
shape
=
x
.
shape
for
other
in
rest
:
if
other
.
shape
!=
shape
:
raise
_assert_same_shapes
.
E_shape
_assert_same_shapes
.
E_shape
=
ValueError
(
"The dimensions of the inputs do not match."
)
def
_assert_tensor_scalar
(
x
,
a
):
class
_Elemwise
(
Elemwise
,
_Op
):
"""ensure that the second input is a scalar"""
if
numpy
.
product
(
a
.
shape
)
!=
1
:
raise
ValueError
(
"The second argument must be a scalar."
)
class
_Elemwise
(
_Op
):
@staticmethod
@staticmethod
def
extract_name
(
name
):
def
extract_name
(
name
):
...
@@ -334,27 +200,20 @@ class _Elemwise(_Op):
...
@@ -334,27 +200,20 @@ class _Elemwise(_Op):
def
is_loop_var
(
name
):
def
is_loop_var
(
name
):
return
name
.
endswith
(
"_i"
)
return
name
.
endswith
(
"_i"
)
def
c_var_names
(
self
):
def
var_desc
(
self
):
cls
=
self
.
__class__
(
self
,
inames
,
onames
),
_1
,
_2
,
_3
=
inspect
.
getargspec
(
self
.
c_foreach
)
spec
=
([
cls
.
extract_name
(
name
)
for
name
in
inames
],
[
cls
.
extract_name
(
name
)
for
name
in
onames
])
return
spec
def
loop_variables
(
self
):
cls
=
self
.
__class__
cls
=
self
.
__class__
(
self
,
inames
,
onames
),
_1
,
_2
,
_3
=
inspect
.
getargspec
(
cls
.
c_foreach
)
(
self
,
inames
,
onames
),
_1
,
_2
,
_3
=
inspect
.
getargspec
(
cls
.
c_foreach
)
return
([
cls
.
extract_name
(
name
)
for
name
in
inames
if
cls
.
is_loop_var
(
name
)
],
return
([
(
cls
.
extract_name
(
name
),
cls
.
is_loop_var
(
name
))
for
name
in
inames
],
[
cls
.
extract_name
(
name
)
for
name
in
onames
if
cls
.
is_loop_var
(
name
)
])
[
(
cls
.
extract_name
(
name
),
cls
.
is_loop_var
(
name
))
for
name
in
onames
])
def
propagate_broadcastable
(
self
,
*
inputs
):
def
propagate_broadcastable
(
self
,
*
inputs
):
i
names
,
onames
=
self
.
c_var_names
()
i
desc
,
odesc
=
self
.
var_desc
()
iloop
,
oloop
=
self
.
loop_variables
()
nonloop_o
=
[
o
[
0
]
for
o
in
odesc
if
not
o
[
1
]]
if
oloop
!=
onames
:
if
nonloop_o
:
raise
Exception
(
\
raise
Exception
(
"Cannot infer broadcastable for non-loop variable(s)
%
s"
%
nonloop_o
)
"Cannot infer broadcastable for non-loop variable(s)
%
s"
\
all_bcast
=
[
broadcastable
for
broadcastable
,
i
in
zip
(
inputs
,
idesc
)
if
i
[
1
]]
%
set
(
onames
)
.
difference
(
oloop
),
self
.
__class__
)
if
reduce
(
lambda
x
,
y
:
x
is
not
False
and
x
==
y
and
y
,
[
len
(
x
)
for
x
in
all_bcast
])
is
False
:
all_bcast
=
[
broadcastable
for
broadcastable
,
iname
in
zip
(
inputs
,
inames
)
if
iname
in
iloop
]
raise
TypeError
(
"Inputs that are loop variables do not all have the same number of dimensions."
)
ret
=
[]
ret
=
[]
for
arr
in
zip
(
*
all_bcast
):
for
arr
in
zip
(
*
all_bcast
):
if
0
in
arr
:
if
0
in
arr
:
...
@@ -363,37 +222,38 @@ class _Elemwise(_Op):
...
@@ -363,37 +222,38 @@ class _Elemwise(_Op):
ret
.
append
(
1
)
ret
.
append
(
1
)
return
[
ret
]
*
self
.
nout
return
[
ret
]
*
self
.
nout
@classmethod
def
inplace_version
(
cls
):
class
Ret
(
cls
,
Destroyer
):
def
destroy_list
(
self
):
return
self
.
inputs
[
0
]
return
Ret
def
c_init
(
self
,
inputs
,
outputs
):
def
c_init
(
self
,
inputs
,
outputs
):
pass
raise
AbstractFunctionError
()
def
c_foreach
(
self
,
inputs
,
outputs
):
def
c_foreach
(
self
,
inputs
,
outputs
):
pass
raise
AbstractFunctionError
()
def
c_finalize
(
self
,
inputs
,
outputs
):
def
c_finalize
(
self
,
inputs
,
outputs
):
pass
raise
AbstractFunctionError
()
def
c_code_init
(
self
):
return
self
.
c_init
(
self
.
inputs
,
self
.
outputs
)
def
c_code_foreach
(
self
):
return
self
.
c_foreach
(
self
.
inputs
,
self
.
outputs
)
def
c_code_finalize
(
self
):
return
self
.
c_finalize
(
self
.
inputs
,
self
.
outputs
)
class
TensorScalarOp
(
_Elemwise
):
class
TensorScalarOp
(
_Elemwise
):
def
c_var_names
(
self
):
def
var_desc
(
self
):
return
([
'x'
,
'_a'
],
[
'z'
,
])
return
[(
'x'
,
1
),
(
'a'
,
0
)],
[(
'z'
,
1
)]
def
loop_variables
(
self
):
def
c_code_init
(
self
):
return
([
'x'
,
],
[
'z'
,
])
def
c_init
((
x
,
_a
),
(
z
,
)):
return
"""
return
"""
if (PyArray_SIZE(
_a
) != 1) {
if (PyArray_SIZE(
%(a)
s
) != 1) {
PyErr_SetString(PyExc_ValueError,
\"
The size of the scalar argument is not 1.
\"
);
PyErr_SetString(PyExc_ValueError,
\"
The size of the scalar argument is not 1.
\"
);
%(fail)
s
}
}
_a_dtype a = ((_a_dtype*)PyArray_DATA(_a
))[0];
dtype_
%(a)
s _
%(a)
s = ((dtype_
%(a)
s*)PyArray_DATA(
%(a)
s
))[0];
"""
"""
def
_c
_foreach
(
self
):
def
c_code
_foreach
(
self
):
return
"
z
_i =
%
s;"
%
self
.
c_expr
return
"
%%(z)
s
_i =
%
s;"
%
self
.
c_expr
def
constructor
(
op_cls
):
def
constructor
(
op_cls
):
def
f
(
*
args
,
**
kwargs
):
def
f
(
*
args
,
**
kwargs
):
...
@@ -404,6 +264,7 @@ def constructor(op_cls):
...
@@ -404,6 +264,7 @@ def constructor(op_cls):
return
op
.
outputs
[
0
]
return
op
.
outputs
[
0
]
return
f
return
f
##########################
##########################
# Unary Operations
# Unary Operations
##########################
##########################
...
@@ -412,9 +273,9 @@ class Abs(_Elemwise):
...
@@ -412,9 +273,9 @@ class Abs(_Elemwise):
def
impl
(
self
,
x
):
def
impl
(
self
,
x
):
return
numpy
.
abs
(
x
)
return
numpy
.
abs
(
x
)
def
grad
(
self
,
x
,
gz
):
def
grad
(
self
,
x
,
gz
):
return
gz
*
Sgn
(
x
)
.
out
#TODO: handle the corner case (get it? pun?)
return
gz
*
Sgn
(
x
)
.
out
#TODO: handle the corner case (get it? pun?)
(there's a special place in hell for people like you)
def
c_foreach
(
self
,
(
x_i
,
),
(
z_i
,
)):
def
c_foreach
(
self
,
(
x_i
,
),
(
z_i
,
)):
return
"
z_i = abs(x
_i);"
return
"
%(z)
s_i = abs(
%(x)
s
_i);"
#Constructor not necessary because builtin abs() does this
#Constructor not necessary because builtin abs() does this
class
Neg
(
_Elemwise
):
class
Neg
(
_Elemwise
):
...
@@ -423,7 +284,7 @@ class Neg(_Elemwise):
...
@@ -423,7 +284,7 @@ class Neg(_Elemwise):
def
grad
(
self
,
x
,
gz
):
def
grad
(
self
,
x
,
gz
):
return
-
gz
return
-
gz
def
c_foreach
(
self
,
(
x_i
,
),
(
z_i
,
)):
def
c_foreach
(
self
,
(
x_i
,
),
(
z_i
,
)):
return
"
z_i = -x
_i;"
return
"
%(z)
s_i = -
%(x)
s
_i;"
#Constructor not necessary because unary '-' does this
#Constructor not necessary because unary '-' does this
class
Sgn
(
_Elemwise
):
class
Sgn
(
_Elemwise
):
...
@@ -432,7 +293,7 @@ class Sgn(_Elemwise):
...
@@ -432,7 +293,7 @@ class Sgn(_Elemwise):
def
grad
(
self
,
x
,
gz
):
def
grad
(
self
,
x
,
gz
):
return
[
None
]
return
[
None
]
def
c_foreach
(
self
,
(
x_i
,
),
(
z_i
,
)):
def
c_foreach
(
self
,
(
x_i
,
),
(
z_i
,
)):
return
"
z_i = x_i/abs(x
_i);"
# TODO: C use copysign
return
"
%(z)
s_i =
%(x)
s_i/abs(
%(x)
s
_i);"
# TODO: C use copysign
sgn
=
constructor
(
Sgn
)
sgn
=
constructor
(
Sgn
)
class
Sum
(
_Elemwise
):
class
Sum
(
_Elemwise
):
...
@@ -443,9 +304,9 @@ class Sum(_Elemwise):
...
@@ -443,9 +304,9 @@ class Sum(_Elemwise):
def
propagate_broadcastable
(
self
,
*
inputs
):
def
propagate_broadcastable
(
self
,
*
inputs
):
return
[()]
return
[()]
def
c_init
(
self
,
(
x
,
),
(
sum
,
)):
def
c_init
(
self
,
(
x
,
),
(
sum
,
)):
return
"
sum_dtype* sump = ((sum_dtype*)PyArray_DATA(sum)); sum
p[0] = 0;"
return
"
dtype_
%(sum)
s*
%(sum)
sp = ((dtype_
%(sum)
s*)PyArray_DATA(
%(sum)
s));
%(sum)
s
p[0] = 0;"
def
c_foreach
(
self
,
(
x_i
,
),
(
sum
,
)):
def
c_foreach
(
self
,
(
x_i
,
),
(
sum
,
)):
return
"
sump[0] += x
_i;"
return
"
%(sum)
sp[0] +=
%(x)
s
_i;"
sum
=
constructor
(
Sum
)
sum
=
constructor
(
Sum
)
class
Fill
(
_Elemwise
):
class
Fill
(
_Elemwise
):
...
@@ -454,9 +315,9 @@ class Fill(_Elemwise):
...
@@ -454,9 +315,9 @@ class Fill(_Elemwise):
def
grad
(
self
,
(
model
,
value
),
gz
):
def
grad
(
self
,
(
model
,
value
),
gz
):
return
None
,
sum
(
gz
)
return
None
,
sum
(
gz
)
def
c_init
(
self
,
(
model
,
value
),
(
z
,
)):
def
c_init
(
self
,
(
model
,
value
),
(
z
,
)):
return
"
value_dtype value0 = ((value_dtype*)PyArray_DATA(value
))[0];"
return
"
dtype_
%(value)
s
%(value)
s0 = ((dtype_
%(value)
s*)PyArray_DATA(
%(value)
s
))[0];"
def
c_foreach
(
self
,
(
model_i
,
value
),
(
z_i
,
)):
def
c_foreach
(
self
,
(
model_i
,
value
),
(
z_i
,
)):
return
"
z_i = value
0;"
return
"
%(z)
s_i =
%(value)
s
0;"
fill
=
constructor
(
Fill
)
fill
=
constructor
(
Fill
)
...
@@ -466,7 +327,7 @@ class TensorCopy(_Elemwise):
...
@@ -466,7 +327,7 @@ class TensorCopy(_Elemwise):
def
grad
(
self
,
x
,
gz
):
def
grad
(
self
,
x
,
gz
):
return
gz
return
gz
def
c_foreach
(
self
,
(
x_i
,
),
(
z_i
,
)):
def
c_foreach
(
self
,
(
x_i
,
),
(
z_i
,
)):
return
"
z_i = x
_i;"
return
"
%(z)
s_i =
%(x)
s
_i;"
tensor_copy
=
constructor
(
TensorCopy
)
tensor_copy
=
constructor
(
TensorCopy
)
if
0
:
if
0
:
...
@@ -597,7 +458,7 @@ class MulElemwise(_Elemwise):
...
@@ -597,7 +458,7 @@ class MulElemwise(_Elemwise):
def
grad
(
self
,
(
x
,
y
),
gz
):
def
grad
(
self
,
(
x
,
y
),
gz
):
return
mul
(
y
,
gz
),
mul
(
x
,
gz
)
return
mul
(
y
,
gz
),
mul
(
x
,
gz
)
def
c_foreach
(
self
,
(
x_i
,
y_i
),
(
z_i
,
)):
def
c_foreach
(
self
,
(
x_i
,
y_i
),
(
z_i
,
)):
return
"
z_i = x_i * y
_i;"
return
"
%(z)
s_i =
%(x)
s_i *
%(y)
s
_i;"
mul_elemwise
=
constructor
(
MulElemwise
)
mul_elemwise
=
constructor
(
MulElemwise
)
class
MulElemwiseInplace
(
MulElemwise
.
inplace_version
()):
class
MulElemwiseInplace
(
MulElemwise
.
inplace_version
()):
...
@@ -614,7 +475,7 @@ class Scale(TensorScalarOp):
...
@@ -614,7 +475,7 @@ class Scale(TensorScalarOp):
return
x
*
a
return
x
*
a
def
grad
(
self
,
(
x
,
a
),
gz
):
def
grad
(
self
,
(
x
,
a
),
gz
):
return
scale
(
a
,
gz
),
sum
(
mul_elemwise
(
x
,
gz
))
return
scale
(
a
,
gz
),
sum
(
mul_elemwise
(
x
,
gz
))
c_expr
=
"
x_i * a
"
c_expr
=
"
%(x)
s_i * _
%(a)
s
"
scale
=
constructor
(
Scale
)
scale
=
constructor
(
Scale
)
class
ScaleInplace
(
Scale
.
inplace_version
()):
class
ScaleInplace
(
Scale
.
inplace_version
()):
...
...
tensor_ops.py
浏览文件 @
d676edaf
...
@@ -6,206 +6,6 @@ from tensor import *
...
@@ -6,206 +6,6 @@ from tensor import *
# # TensorOp is a convenient base class, permitting to factor the code for the
# # Ops in this file.
# # It is not necessary to inherit from TensorOp to make an Op that manipulates
# # Tensors.
# <<<<<<< /u/breuleuo/hg/new_theano/tensor_ops.py
# class TensorOp(Op):
# nin = -1
# nout = 1
# cast_method = lambda self, *args: _upcast(*args)
# def __init__(self, *inputs):
# inputs = map(_wrap_as_tensor, inputs)
# if self.nin >= 0:
# if len(inputs) != self.nin:
# raise TypeError("Wrong number of inputs for %s (got %i, expected %i)") \
# % (self, len(inputs), self.nin)
# i_broadcastables = [getattr(input, 'broadcastable', None) for input in inputs]
# i_dtypes = [getattr(input, 'dtype', None) for input in inputs]
# o_broadcastables = utils.from_return_values(self.propagate_broadcastable(*i_broadcastables))
# o_dtypes = utils.from_return_values(self.propagate_dtype(*i_dtypes))
# self.inputs = inputs
# self.outputs = [Tensor(dtype, broadcastable) for broadcastable, dtype in zip(o_broadcastables, o_dtypes)]
# def propagate_broadcastable(self, *inputs):
# raise AbstractFunctionError()
# def propagate_dtype(self, *i_dtypes):
# for dtype in i_dtypes:
# if dtype is None:
# raise TypeError("Expected a Tensor.")
# return self.cast_method(*i_dtypes)
# def impl(self, *inputs):
# raise AbstractFunctionError()
# def perform(self):
# res = self.impl(*[input.data for input in self.inputs])
# if self.nout == 1:
# self.outputs[0].data = res
# else:
# for output, value in zip(self.outputs, res):
# output.data = value
# def c_var_names(self):
# (self, inames, onames), _1, _2, _3 = inspect.getargspec(self.c_impl)
# inames = utils.from_return_values(inames)
# onames = utils.from_return_values(onames)
# return [inames, onames]
# def c_code(self):
# return self.c_impl(self.inputs, self.outputs)
# def c_impl(self, inputs, outputs):
# raise AbstractFunctionError()
# class UnaryTensorOp(TensorOp):
# nin = 1
# class BinaryTensorOp(TensorOp):
# nin = 2
# # class Transpose(UnaryTensorOp):
# # def propagate_broadcastable(self, x):
# # x2 = copy(x)
# # x2.reverse()
# # return [x2]
# # def impl(self, x):
# # return x.T
# # def c_impl(self, x, z):
# # return """
# # PyArrayObject* transposed = (PyArrayObject*)PyArray_Transpose(%(x)s, NULL);
# # //if (PyArray_REFCOUNT(transposed) == 1) {
# # // printf("lala\\n");
# # //}
# # //if (%(z)s) {
# # // Py_XDECREF(%(z)s);
# # //}
# # %(z)s = transposed;
# # Py_XINCREF(%(z)s);
# # """
# def scalar_switch(normal_f, scalar_f, scalar_f_reverse = None):
# def f(x, y):
# x, y = _wrap_as_tensor(x), _wrap_as_tensor(y)
# if 0 not in y.broadcastable:
# return scalar_f(x, y)
# if 0 not in x.broadcastable:
# if scalar_f_reverse:
# return scalar_f_reverse(y, x)
# else:
# raise TypeError("You cannot do this operation on a scalar.")
# return normal_f(x, y)
# return f
# # Wrapper to ensure that all inputs to the function impl have the same size (foils numpy's broadcasting)
# def assert_same_shapes(x, *rest):
# shape = x.shape
# for other in rest:
# if other.shape != shape:
# raise ValueError("The dimensions of the inputs do not match.")
# # Wrapper to ensure that the last input to impl is a scalar
# def assert_tensor_scalar(x, a):
# if numpy.product(a.shape) != 1:
# raise ValueError("The second argument must be a scalar.")
# class Elemwise(TensorOp):
# @staticmethod
# def extract_name(name):
# if name.endswith("_i"):
# return name[:-2]
# else:
# return name
# @staticmethod
# def is_loop_var(name):
# return name.endswith("_i")
# def c_var_names(self):
# cls = self.__class__
# (self, inames, onames), _1, _2, _3 = inspect.getargspec(self.c_foreach)
# spec = ([cls.extract_name(name) for name in inames],
# [cls.extract_name(name) for name in onames])
# return spec
# def loop_variables(self):
# cls = self.__class__
# (self, inames, onames), _1, _2, _3 = inspect.getargspec(cls.c_foreach)
# return ([cls.extract_name(name) for name in inames if cls.is_loop_var(name)],
# [cls.extract_name(name) for name in onames if cls.is_loop_var(name)])
# def propagate_broadcastable(self, *inputs):
# inames, onames = self.c_var_names()
# iloop, oloop = self.loop_variables()
# if oloop != onames:
# raise Exception("Cannot infer broadcastable for non-loop variable(s) %s" % set(onames).difference(oloop))
# all_bcast = [broadcastable for broadcastable, iname in zip(inputs, inames) if iname in iloop]
# ret = []
# for arr in zip(*all_bcast):
# if 0 in arr:
# ret.append(0)
# else:
# ret.append(1)
# return [ret] * self.nout
# @classmethod
# def inplace_version(cls):
# class Ret(cls, Destroyer):
# def destroy_list(self):
# return self.inputs[0]
# return Ret
# def c_init(self, inputs, outputs):
# pass
# def c_foreach(self, inputs, outputs):
# pass
# def c_finalize(self, inputs, outputs):
# pass
# class TensorScalarOp(Elemwise):
# def c_var_names(self):
# return (['x', '_a'], ['z', ])
# def loop_variables(self):
# return (['x', ], ['z', ])
# def c_init((x, _a), (z, )):
# return """
# if (PyArray_SIZE(_a) != 1) {
# PyErr_SetString(PyExc_ValueError, \"The size of the scalar argument is not 1.\");
# }
# _a_dtype a = ((_a_dtype*)PyArray_DATA(_a))[0];
# """
# def _c_foreach(self):
# return "z_i = %s;" % self.c_expr
# =======
# >>>>>>> /tmp/tensor_ops.py~other.fNA50a
###########################
###########################
#### Binary Operations ####
#### Binary Operations ####
###########################
###########################
...
@@ -214,6 +14,11 @@ from tensor import *
...
@@ -214,6 +14,11 @@ from tensor import *
## Dot ##
## Dot ##
#########
#########
class
Dot
(
TensorOp
):
class
Dot
(
TensorOp
):
@staticmethod
@staticmethod
def
_output_shape
(
xshape
,
yshape
):
def
_output_shape
(
xshape
,
yshape
):
...
@@ -258,20 +63,12 @@ class Dot(TensorOp):
...
@@ -258,20 +63,12 @@ class Dot(TensorOp):
class
Neg
(
Elemwise
):
def
impl
(
self
,
x
):
return
-
x
def
grad
(
self
,
x
,
gz
):
return
-
gz
def
c_foreach
(
self
,
(
x_i
,
),
(
z_i
,
)):
return
"z_i = -x_i;"
class
NegInplace
(
Neg
.
inplace_version
()):
class
NegInplace
(
Neg
.
inplace_version
()):
def
impl
(
self
,
x
):
def
impl
(
self
,
x
):
x
*=
-
1
x
*=
-
1
return
x
return
x
class
InvElemwise
(
Elemwise
):
class
InvElemwise
(
Elemwise
):
def
impl
(
self
,
x
):
def
impl
(
self
,
x
):
return
1
/
x
return
1
/
x
...
@@ -425,3 +222,28 @@ class MinMax:
...
@@ -425,3 +222,28 @@ class MinMax:
# # class Transpose(UnaryTensorOp):
# # def propagate_broadcastable(self, x):
# # x2 = copy(x)
# # x2.reverse()
# # return [x2]
# # def impl(self, x):
# # return x.T
# # def c_impl(self, x, z):
# # return """
# # PyArrayObject* transposed = (PyArrayObject*)PyArray_Transpose(%(x)s, NULL);
# # //if (PyArray_REFCOUNT(transposed) == 1) {
# # // printf("lala\\n");
# # //}
# # //if (%(z)s) {
# # // Py_XDECREF(%(z)s);
# # //}
# # %(z)s = transposed;
# # Py_XINCREF(%(z)s);
# # """
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论