Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
P
pytensor
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
testgroup
pytensor
Commits
cb823769
提交
cb823769
authored
4月 18, 2008
作者:
Olivier Breuleux
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
merged base_tensor with tensor
上级
55f4d322
隐藏空白字符变更
内嵌
并排
正在显示
7 个修改的文件
包含
422 行增加
和
497 行删除
+422
-497
__init__.py
__init__.py
+0
-2
_test_base_tensor.py
_test_base_tensor.py
+0
-142
_test_tensor.py
_test_tensor.py
+137
-0
base_tensor.py
base_tensor.py
+0
-312
elemwise.py
elemwise.py
+0
-1
sparse.py
sparse.py
+8
-8
tensor.py
tensor.py
+277
-32
没有找到文件。
__init__.py
浏览文件 @
cb823769
import
gof
import
gof
import
base_tensor
import
tensor
import
tensor
import
sparse
import
sparse
import
compile
import
compile
import
gradient
import
gradient
import
opt
import
opt
from
base_tensor
import
*
from
tensor
import
*
from
tensor
import
*
from
compile
import
*
from
compile
import
*
from
opt
import
*
from
opt
import
*
...
...
_test_base_tensor.py
deleted
100644 → 0
浏览文件 @
55f4d322
from
base_tensor
import
*
import
unittest
from
copy
import
copy
from
compile
import
Function
import
gof
def
_tensor
(
data
,
broadcastable
=
None
,
name
=
None
):
"""Return a BaseTensor containing given data"""
data
=
numpy
.
asarray
(
data
)
if
broadcastable
is
None
:
broadcastable
=
[
s
==
1
for
s
in
data
.
shape
]
elif
broadcastable
in
[
0
,
1
]:
broadcastable
=
[
broadcastable
]
*
len
(
data
.
shape
)
rval
=
BaseTensor
(
data
.
dtype
,
broadcastable
,
name
)
rval
.
data
=
data
# will raise if broadcastable was mis-specified
return
rval
class
T_tensor
(
unittest
.
TestCase
):
def
test0
(
self
):
# allocate from a scalar float
t
=
_tensor
(
1.0
)
self
.
failUnless
(
isinstance
(
t
,
BaseTensor
))
self
.
failUnless
(
t
.
dtype
==
'float64'
)
self
.
failUnless
(
t
.
broadcastable
==
())
self
.
failUnless
(
t
.
role
==
None
)
self
.
failUnless
(
isinstance
(
t
.
data
,
numpy
.
ndarray
))
self
.
failUnless
(
str
(
t
.
data
.
dtype
)
==
'float64'
)
self
.
failUnless
(
t
.
data
==
1.0
)
def
test0_int
(
self
):
# allocate from a scalar float
t
=
_tensor
(
1
)
self
.
failUnless
(
isinstance
(
t
,
BaseTensor
))
self
.
failUnless
(
t
.
dtype
==
'int64'
or
t
.
dtype
==
'int32'
)
def
test1
(
self
):
# allocate from a vector of ints, not broadcastable
t
=
_tensor
(
numpy
.
ones
(
5
,
dtype
=
'int32'
))
self
.
failUnless
(
isinstance
(
t
,
BaseTensor
))
self
.
failUnless
(
t
.
dtype
==
'int32'
)
self
.
failUnless
(
t
.
broadcastable
==
(
0
,))
self
.
failUnless
(
isinstance
(
t
.
data
,
numpy
.
ndarray
))
self
.
failUnless
(
str
(
t
.
data
.
dtype
)
==
'int32'
)
def
test2
(
self
):
# allocate from a column matrix of complex with name
t
=
_tensor
(
numpy
.
ones
((
5
,
1
),
dtype
=
'complex64'
),
name
=
'bart'
)
self
.
failUnless
(
isinstance
(
t
,
BaseTensor
))
self
.
failUnless
(
t
.
dtype
==
'complex64'
)
self
.
failUnless
(
t
.
broadcastable
==
(
0
,
1
))
self
.
failUnless
(
isinstance
(
t
.
data
,
numpy
.
ndarray
))
self
.
failUnless
(
t
.
name
==
'bart'
)
def
test2b
(
self
):
# allocate from a column matrix, not broadcastable
t
=
_tensor
(
numpy
.
ones
((
5
,
1
),
dtype
=
'complex64'
),
broadcastable
=
0
)
self
.
failUnless
(
isinstance
(
t
,
BaseTensor
))
self
.
failUnless
(
t
.
dtype
==
'complex64'
)
self
.
failUnless
(
t
.
broadcastable
==
(
0
,
0
))
self
.
failUnless
(
isinstance
(
t
.
data
,
numpy
.
ndarray
))
f
=
Function
([
t
],
[
t
],
linker_cls
=
gof
.
CLinker
)
self
.
failUnless
(
numpy
.
all
(
t
.
data
==
f
(
t
.
data
)))
def
test_data_normal
(
self
):
#test that assigning to .data works when it should
t
=
_tensor
(
numpy
.
ones
((
5
,
1
),
dtype
=
'complex64'
),
broadcastable
=
0
)
o27
=
numpy
.
ones
((
2
,
7
),
dtype
=
'complex64'
)
t
.
data
=
o27
lst
=
t
.
_data
self
.
failUnless
(
t
.
data
.
shape
==
(
2
,
7
))
self
.
failUnless
(
t
.
data
is
o27
)
self
.
failUnless
(
t
.
_data
is
lst
)
def
test_data_badrank0
(
self
):
t
=
_tensor
(
numpy
.
ones
((
5
,
1
),
dtype
=
'complex64'
),
broadcastable
=
0
)
try
:
t
.
data
=
numpy
.
ones
((
2
,
7
,
1
))
self
.
fail
()
except
ValueError
,
e
:
self
.
failUnless
(
e
[
0
]
is
BaseTensor
.
filter
.
E_rank
)
try
:
t
.
data
=
numpy
.
ones
(
1
)
self
.
fail
()
except
ValueError
,
e
:
self
.
failUnless
(
e
[
0
]
is
BaseTensor
.
filter
.
E_rank
)
def
test_data_badrank1
(
self
):
t
=
_tensor
(
numpy
.
ones
((
1
,
1
),
dtype
=
'complex64'
),
broadcastable
=
1
)
try
:
t
.
data
=
numpy
.
ones
((
1
,
1
,
1
))
self
.
fail
()
except
ValueError
,
e
:
self
.
failUnless
(
e
[
0
]
is
BaseTensor
.
filter
.
E_rank
)
try
:
t
.
data
=
numpy
.
ones
(
1
)
self
.
fail
()
except
ValueError
,
e
:
self
.
failUnless
(
e
[
0
]
is
BaseTensor
.
filter
.
E_rank
)
def
test_data_badshape0
(
self
):
t
=
_tensor
(
numpy
.
ones
((
1
,
1
),
dtype
=
'complex64'
),
broadcastable
=
1
)
try
:
t
.
data
=
numpy
.
ones
((
1
,
2
))
self
.
fail
()
except
ValueError
,
e
:
self
.
failUnless
(
e
[
0
]
is
BaseTensor
.
filter
.
E_shape
)
try
:
t
.
data
=
numpy
.
ones
((
0
,
1
))
self
.
fail
()
except
ValueError
,
e
:
self
.
failUnless
(
e
[
0
]
is
BaseTensor
.
filter
.
E_shape
)
def
test_cast0
(
self
):
t
=
BaseTensor
(
'float32'
,
[
0
])
t
.
data
=
numpy
.
random
.
rand
(
4
)
>
0.5
self
.
failUnless
(
str
(
t
.
data
.
dtype
)
==
t
.
dtype
)
class
T_stdlib
(
unittest
.
TestCase
):
def
test0
(
self
):
t
=
_tensor
(
1.0
)
tt
=
t
.
clone
(
False
)
self
.
failUnless
(
t
.
dtype
==
tt
.
dtype
)
self
.
failUnless
(
t
.
broadcastable
is
tt
.
broadcastable
)
self
.
failUnless
(
tt
.
data
is
None
)
self
.
failUnless
(
t
.
data
==
1.0
)
def
test0b
(
self
):
t
=
_tensor
(
1.0
)
tt
=
t
.
clone
()
self
.
failUnless
(
t
.
dtype
==
tt
.
dtype
)
self
.
failUnless
(
t
.
broadcastable
is
tt
.
broadcastable
)
self
.
failUnless
(
tt
.
data
is
None
)
self
.
failUnless
(
t
.
data
==
1.0
)
def
test1
(
self
):
t
=
_tensor
(
1.0
)
tt
=
t
.
clone
(
True
)
self
.
failUnless
(
t
.
dtype
==
tt
.
dtype
)
self
.
failUnless
(
t
.
broadcastable
is
tt
.
broadcastable
)
self
.
failUnless
(
tt
.
data
==
1.0
)
self
.
failUnless
(
t
.
data
==
1.0
)
self
.
failUnless
(
t
.
data
is
not
tt
.
data
)
def
test1b
(
self
):
t
=
_tensor
(
1.0
)
tt
=
copy
(
t
)
self
.
failUnless
(
t
.
dtype
==
tt
.
dtype
)
self
.
failUnless
(
t
.
broadcastable
is
tt
.
broadcastable
)
self
.
failUnless
(
tt
.
data
==
1.0
)
self
.
failUnless
(
t
.
data
==
1.0
)
self
.
failUnless
(
t
.
data
is
not
tt
.
data
)
if
__name__
==
'__main__'
:
unittest
.
main
()
_test_tensor.py
浏览文件 @
cb823769
...
@@ -1280,5 +1280,142 @@ class t_gemm(unittest.TestCase):
...
@@ -1280,5 +1280,142 @@ class t_gemm(unittest.TestCase):
self
.
fail
()
self
.
fail
()
def
_tensor
(
data
,
broadcastable
=
None
,
name
=
None
):
"""Return a Tensor containing given data"""
data
=
numpy
.
asarray
(
data
)
if
broadcastable
is
None
:
broadcastable
=
[
s
==
1
for
s
in
data
.
shape
]
elif
broadcastable
in
[
0
,
1
]:
broadcastable
=
[
broadcastable
]
*
len
(
data
.
shape
)
rval
=
Tensor
(
data
.
dtype
,
broadcastable
,
name
)
rval
.
data
=
data
# will raise if broadcastable was mis-specified
return
rval
class
T_tensor
(
unittest
.
TestCase
):
def
test0
(
self
):
# allocate from a scalar float
t
=
_tensor
(
1.0
)
self
.
failUnless
(
isinstance
(
t
,
Tensor
))
self
.
failUnless
(
t
.
dtype
==
'float64'
)
self
.
failUnless
(
t
.
broadcastable
==
())
self
.
failUnless
(
t
.
role
==
None
)
self
.
failUnless
(
isinstance
(
t
.
data
,
numpy
.
ndarray
))
self
.
failUnless
(
str
(
t
.
data
.
dtype
)
==
'float64'
)
self
.
failUnless
(
t
.
data
==
1.0
)
def
test0_int
(
self
):
# allocate from a scalar float
t
=
_tensor
(
1
)
self
.
failUnless
(
isinstance
(
t
,
Tensor
))
self
.
failUnless
(
t
.
dtype
==
'int64'
or
t
.
dtype
==
'int32'
)
def
test1
(
self
):
# allocate from a vector of ints, not broadcastable
t
=
_tensor
(
numpy
.
ones
(
5
,
dtype
=
'int32'
))
self
.
failUnless
(
isinstance
(
t
,
Tensor
))
self
.
failUnless
(
t
.
dtype
==
'int32'
)
self
.
failUnless
(
t
.
broadcastable
==
(
0
,))
self
.
failUnless
(
isinstance
(
t
.
data
,
numpy
.
ndarray
))
self
.
failUnless
(
str
(
t
.
data
.
dtype
)
==
'int32'
)
def
test2
(
self
):
# allocate from a column matrix of complex with name
t
=
_tensor
(
numpy
.
ones
((
5
,
1
),
dtype
=
'complex64'
),
name
=
'bart'
)
self
.
failUnless
(
isinstance
(
t
,
Tensor
))
self
.
failUnless
(
t
.
dtype
==
'complex64'
)
self
.
failUnless
(
t
.
broadcastable
==
(
0
,
1
))
self
.
failUnless
(
isinstance
(
t
.
data
,
numpy
.
ndarray
))
self
.
failUnless
(
t
.
name
==
'bart'
)
def
test2b
(
self
):
# allocate from a column matrix, not broadcastable
t
=
_tensor
(
numpy
.
ones
((
5
,
1
),
dtype
=
'complex64'
),
broadcastable
=
0
)
self
.
failUnless
(
isinstance
(
t
,
Tensor
))
self
.
failUnless
(
t
.
dtype
==
'complex64'
)
self
.
failUnless
(
t
.
broadcastable
==
(
0
,
0
))
self
.
failUnless
(
isinstance
(
t
.
data
,
numpy
.
ndarray
))
f
=
Function
([
t
],
[
t
],
linker_cls
=
gof
.
CLinker
)
self
.
failUnless
(
numpy
.
all
(
t
.
data
==
f
(
t
.
data
)))
def
test_data_normal
(
self
):
#test that assigning to .data works when it should
t
=
_tensor
(
numpy
.
ones
((
5
,
1
),
dtype
=
'complex64'
),
broadcastable
=
0
)
o27
=
numpy
.
ones
((
2
,
7
),
dtype
=
'complex64'
)
t
.
data
=
o27
lst
=
t
.
_data
self
.
failUnless
(
t
.
data
.
shape
==
(
2
,
7
))
self
.
failUnless
(
t
.
data
is
o27
)
self
.
failUnless
(
t
.
_data
is
lst
)
def
test_data_badrank0
(
self
):
t
=
_tensor
(
numpy
.
ones
((
5
,
1
),
dtype
=
'complex64'
),
broadcastable
=
0
)
try
:
t
.
data
=
numpy
.
ones
((
2
,
7
,
1
))
self
.
fail
()
except
ValueError
,
e
:
self
.
failUnless
(
e
[
0
]
is
Tensor
.
filter
.
E_rank
)
try
:
t
.
data
=
numpy
.
ones
(
1
)
self
.
fail
()
except
ValueError
,
e
:
self
.
failUnless
(
e
[
0
]
is
Tensor
.
filter
.
E_rank
)
def
test_data_badrank1
(
self
):
t
=
_tensor
(
numpy
.
ones
((
1
,
1
),
dtype
=
'complex64'
),
broadcastable
=
1
)
try
:
t
.
data
=
numpy
.
ones
((
1
,
1
,
1
))
self
.
fail
()
except
ValueError
,
e
:
self
.
failUnless
(
e
[
0
]
is
Tensor
.
filter
.
E_rank
)
try
:
t
.
data
=
numpy
.
ones
(
1
)
self
.
fail
()
except
ValueError
,
e
:
self
.
failUnless
(
e
[
0
]
is
Tensor
.
filter
.
E_rank
)
def
test_data_badshape0
(
self
):
t
=
_tensor
(
numpy
.
ones
((
1
,
1
),
dtype
=
'complex64'
),
broadcastable
=
1
)
try
:
t
.
data
=
numpy
.
ones
((
1
,
2
))
self
.
fail
()
except
ValueError
,
e
:
self
.
failUnless
(
e
[
0
]
is
Tensor
.
filter
.
E_shape
)
try
:
t
.
data
=
numpy
.
ones
((
0
,
1
))
self
.
fail
()
except
ValueError
,
e
:
self
.
failUnless
(
e
[
0
]
is
Tensor
.
filter
.
E_shape
)
def
test_cast0
(
self
):
t
=
Tensor
(
'float32'
,
[
0
])
t
.
data
=
numpy
.
random
.
rand
(
4
)
>
0.5
self
.
failUnless
(
str
(
t
.
data
.
dtype
)
==
t
.
dtype
)
class
T_stdlib
(
unittest
.
TestCase
):
def
test0
(
self
):
t
=
_tensor
(
1.0
)
tt
=
t
.
clone
(
False
)
self
.
failUnless
(
t
.
dtype
==
tt
.
dtype
)
self
.
failUnless
(
t
.
broadcastable
is
tt
.
broadcastable
)
self
.
failUnless
(
tt
.
data
is
None
)
self
.
failUnless
(
t
.
data
==
1.0
)
def
test0b
(
self
):
t
=
_tensor
(
1.0
)
tt
=
t
.
clone
()
self
.
failUnless
(
t
.
dtype
==
tt
.
dtype
)
self
.
failUnless
(
t
.
broadcastable
is
tt
.
broadcastable
)
self
.
failUnless
(
tt
.
data
is
None
)
self
.
failUnless
(
t
.
data
==
1.0
)
def
test1
(
self
):
t
=
_tensor
(
1.0
)
tt
=
t
.
clone
(
True
)
self
.
failUnless
(
t
.
dtype
==
tt
.
dtype
)
self
.
failUnless
(
t
.
broadcastable
is
tt
.
broadcastable
)
self
.
failUnless
(
tt
.
data
==
1.0
)
self
.
failUnless
(
t
.
data
==
1.0
)
self
.
failUnless
(
t
.
data
is
not
tt
.
data
)
def
test1b
(
self
):
t
=
_tensor
(
1.0
)
tt
=
copy
(
t
)
self
.
failUnless
(
t
.
dtype
==
tt
.
dtype
)
self
.
failUnless
(
t
.
broadcastable
is
tt
.
broadcastable
)
self
.
failUnless
(
tt
.
data
==
1.0
)
self
.
failUnless
(
t
.
data
==
1.0
)
self
.
failUnless
(
t
.
data
is
not
tt
.
data
)
if
__name__
==
'__main__'
:
if
__name__
==
'__main__'
:
unittest
.
main
()
unittest
.
main
()
base_tensor.py
deleted
100644 → 0
浏览文件 @
55f4d322
"""
A simple class to store L{numpy.ndarray} data
"""
from
gof
import
Result
,
Op
,
utils
,
AbstractFunctionError
import
numpy
from
copy
import
copy
###########################
# BaseTensor Class
###########################
class
BaseTensor
(
Result
):
"""
L{Result} to store L{numpy.ndarray} or equivalent via .data
This class does not implement python operators and has no dependencies
on the L{Op}s that use it.
@todo: At some point we should document a glossary, such as terms like
broadcasting and shape.
@type _dtype: numpy dtype string such as 'int64' or 'float64' (among others)
@type _broadcastable: tuple or list or array of boolean values, whose length
is the number of dimensions of the contained L{ndarray}.
@ivar _broadcastable: Each element of the broadcastable vector tells us
something about the corresponding dimension:
- False means the dimension can be anything.
- True means the dimension must be 1. Also, this dimension will be considered
for L{broadcasting}, as described and implemented in Numpy.
"""
def
__init__
(
self
,
dtype
,
broadcastable
,
name
=
None
):
"""Initialize a L{BaseTensor}
@note: This does not actually allocate any data.
"""
# 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.
Result
.
__init__
(
self
,
role
=
None
,
name
=
name
)
self
.
_dtype
=
str
(
dtype
)
self
.
dtype_specs
()
# this is just for error checking
self
.
_broadcastable
=
tuple
(
broadcastable
)
######################
# Result interface
######################
#
# filter
#
def
filter
(
self
,
arr
):
"""Cast to an L{numpy.ndarray} and ensure arr has correct rank and shape."""
if
not
(
isinstance
(
arr
,
numpy
.
ndarray
)
\
and
arr
.
dtype
==
self
.
dtype
):
arr
=
numpy
.
asarray
(
arr
,
dtype
=
self
.
dtype
)
if
len
(
self
.
broadcastable
)
!=
len
(
arr
.
shape
):
raise
ValueError
(
BaseTensor
.
filter
.
E_rank
,
self
.
broadcastable
,
arr
.
shape
,
self
.
owner
)
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
L{self.dtype}. It is for use in C code generation.
"""
#TODO: add more type correspondances for e.g. int32, int64, float32,
#complex64, etc.
try
:
return
{
'float32'
:
(
float
,
'npy_float32'
,
'NPY_FLOAT32'
),
'float64'
:
(
float
,
'npy_float64'
,
'NPY_FLOAT64'
),
'int8'
:
(
int
,
'npy_int8'
,
'NPY_INT8'
),
'int16'
:
(
int
,
'npy_int16'
,
'NPY_INT16'
),
'int32'
:
(
int
,
'npy_int32'
,
'NPY_INT32'
),
'int64'
:
(
int
,
'npy_int64'
,
'NPY_INT64'
),
'complex128'
:
(
complex
,
'theano_complex128'
,
'NPY_COMPLEX128'
),
'complex64'
:
(
complex
,
'theano_complex64'
,
'NPY_COMPLEX64'
)}[
self
.
dtype
]
except
KeyError
:
raise
TypeError
(
"Unsupported dtype for
%
s:
%
s"
%
(
self
.
__class__
.
__name__
,
self
.
dtype
))
#
# Description for constant folding
#
def
desc
(
self
):
"""
Returns a hashable description of this L{BaseTensor}.
"""
if
self
.
data
is
not
None
:
return
(
BaseTensor
,
self
.
dtype
,
self
.
broadcastable
,
self
.
data
.
data
[:])
else
:
return
(
BaseTensor
,
self
.
dtype
,
self
.
broadcastable
,
None
)
#
# C codegen stubs
#
def
c_declare
(
self
,
name
,
sub
):
return
"""
PyArrayObject*
%(name)
s;
int type_num_
%(name)
s;
typedef
%(dtype)
s dtype_
%(name)
s;
"""
%
dict
(
sub
,
name
=
name
,
dtype
=
self
.
dtype_specs
()[
1
])
def
c_init
(
self
,
name
,
sub
):
return
"""
%(name)
s = NULL;
type_num_
%(name)
s =
%(type_num)
s;
"""
%
dict
(
sub
,
name
=
name
,
type_num
=
self
.
dtype_specs
()[
2
])
def
c_extract
(
self
,
name
,
sub
):
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
(
sub
,
name
=
name
,
type_num
=
self
.
dtype_specs
()[
2
])
def
c_cleanup
(
self
,
name
,
sub
):
return
"""
if (
%(name)
s) {
Py_XDECREF(
%(name)
s);
}
"""
%
locals
()
def
c_sync
(
self
,
name
,
sub
):
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);
}
"""
%
locals
()
def
c_headers
(
self
):
return
[]
def
c_libraries
(
self
):
return
[]
def
c_support_code
(
cls
):
template
=
"""
struct theano_complex
%(nbits)
s : public npy_complex
%(nbits)
s
{
typedef theano_complex
%(nbits)
s complex_type;
typedef npy_float
%(half_nbits)
s scalar_type;
complex_type operator +(complex_type y) {
complex_type ret;
ret.real = this->real + y.real;
ret.imag = this->imag + y.imag;
return ret;
}
complex_type operator -(complex_type y) {
complex_type ret;
ret.real = this->real - y.real;
ret.imag = this->imag - y.imag;
return ret;
}
complex_type operator *(complex_type y) {
complex_type ret;
ret.real = this->real * y.real - this->imag * y.imag;
ret.imag = this->real * y.imag + this->imag * y.real;
return ret;
}
complex_type operator /(complex_type y) {
complex_type ret;
scalar_type y_norm_square = y.real * y.real + y.imag * y.imag;
ret.real = (this->real * y.real + this->imag * y.imag) / y_norm_square;
ret.imag = (this->imag * y.real - this->real * y.imag) / y_norm_square;
return ret;
}
};
"""
return
template
%
dict
(
nbits
=
64
,
half_nbits
=
32
)
+
template
%
dict
(
nbits
=
128
,
half_nbits
=
64
)
# todo: use C templating
############################
# Tensor specific attributes
############################
dtype
=
property
(
lambda
self
:
self
.
_dtype
,
doc
=
"read-only access to _dtype, which should not be changed"
)
broadcastable
=
property
(
lambda
self
:
self
.
_broadcastable
,
doc
=
"read-only access to _broadcastable, which should not be changed"
)
############################
# Cloning facilities
############################
def
__copy__
(
self
):
return
self
.
clone
(
True
)
def
clone
(
self
,
transfer_data
=
False
):
"""Return a copy of this instance (with its own attributes)
If transfer_data is True, a copy of self.data is assigned to the copy's
data property, otherwise the copy's data is left as None.
"""
cpy
=
self
.
__class__
(
self
.
dtype
,
self
.
broadcastable
,
self
.
name
)
if
transfer_data
:
cpy
.
data
=
copy
(
self
.
data
)
return
cpy
class
BaseTensorOp
(
Op
):
"""
A basic L{Op} subclass that can be used to make L{Op}s that operate on L{Tensor}s.
It is not mandatory to inherit from this class, but it is practical.
@ivar nin: number of inputs
@ivar nout: number of outputs
@ivar out_tensor_class: L{BaseTensor} subclass used to instantiate the outputs
- input_wrapper: returns a L{Tensor} from its argument
- propagate_dtype: returns a list of dtypes corresponding to the
output dtypes from a list of input dtypes (if an input is not a
L{Tensor}, the passed value will be None)
- propagate_broadcastable: returns a list of tuples corresponding
to the output broadcastable flags from the input broadcastable flags
(if an input is not a L{Tensor}, the passed value will be None).
"""
nin
=
-
1
# nin == -1 means: arbitrary number of inputs
nout
=
1
out_tensor_class
=
BaseTensor
@classmethod
def
input_wrapper
(
cls
,
obj
):
"""
Returns a L{Result} from an arbitrary-typed input, if possible.
"""
if
isinstance
(
obj
,
BaseResult
):
return
obj
else
:
raise
TypeError
(
"Expected a Result instance."
)
def
__init__
(
self
,
*
inputs
):
inputs
=
map
(
self
.
input_wrapper
,
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
=
[
self
.
out_tensor_class
(
dtype
,
broadcastable
)
for
broadcastable
,
dtype
in
zip
(
o_broadcastables
,
o_dtypes
)]
def
propagate_broadcastable
(
self
,
*
inputs
):
raise
AbstractFunctionError
()
def
propagate_dtype
(
self
,
*
i_dtypes
):
rval
=
set
([
dtype
for
dtype
in
i_dtypes
if
dtype
is
not
None
])
if
len
(
rval
)
==
0
:
raise
ValueError
(
"Cannot infer the dtypes of the outputs with no Tensor inputs."
)
elif
len
(
rval
)
>
1
:
raise
ValueError
(
"The dtypes of all inputs should be identical."
)
return
[
rval
.
pop
()]
*
self
.
nout
elemwise.py
浏览文件 @
cb823769
...
@@ -3,7 +3,6 @@ import elemwise_cgen as cgen
...
@@ -3,7 +3,6 @@ import elemwise_cgen as cgen
import
numpy
import
numpy
from
gof
import
Op
,
Viewer
,
Destroyer
from
gof
import
Op
,
Viewer
,
Destroyer
#from base_tensor import BaseTensor as Tensor
import
scalar
import
scalar
from
scalar
import
upcast
,
Scalar
from
scalar
import
upcast
,
Scalar
import
gof
import
gof
...
...
sparse.py
浏览文件 @
cb823769
...
@@ -11,7 +11,7 @@ import numpy
...
@@ -11,7 +11,7 @@ import numpy
from
scipy
import
sparse
from
scipy
import
sparse
import
gof.op
,
gof
.
result
import
gof.op
,
gof
.
result
import
tensor
,
base_tensor
import
tensor
...
@@ -20,19 +20,19 @@ import tensor, base_tensor
...
@@ -20,19 +20,19 @@ import tensor, base_tensor
def
_is_sparse_result
(
x
):
def
_is_sparse_result
(
x
):
"""
"""
@rtype: boolean
@rtype: boolean
@return: True iff x is a L{SparseResult} (and not a L{
base_tensor.Base
Tensor})
@return: True iff x is a L{SparseResult} (and not a L{
tensor.
Tensor})
"""
"""
if
not
isinstance
(
x
,
SparseResult
)
and
not
isinstance
(
x
,
base_tensor
.
Base
Tensor
):
if
not
isinstance
(
x
,
SparseResult
)
and
not
isinstance
(
x
,
tensor
.
Tensor
):
raise
NotImplementedError
(
"_is_sparse should only be called on sparse.SparseResult or
base_tensor.Base
Tensor, not,"
,
x
)
raise
NotImplementedError
(
"_is_sparse should only be called on sparse.SparseResult or
tensor.
Tensor, not,"
,
x
)
return
isinstance
(
x
,
SparseResult
)
return
isinstance
(
x
,
SparseResult
)
def
_is_dense_result
(
x
):
def
_is_dense_result
(
x
):
"""
"""
@rtype: boolean
@rtype: boolean
@return: True unless x is a L{SparseResult} (and not a L{
base_tensor.Base
Tensor})
@return: True unless x is a L{SparseResult} (and not a L{
tensor.
Tensor})
"""
"""
if
not
isinstance
(
x
,
SparseResult
)
and
not
isinstance
(
x
,
base_tensor
.
Base
Tensor
):
if
not
isinstance
(
x
,
SparseResult
)
and
not
isinstance
(
x
,
tensor
.
Tensor
):
raise
NotImplementedError
(
"_is_sparse should only be called on sparse.SparseResult or
base_tensor.Base
Tensor, not,"
,
x
)
raise
NotImplementedError
(
"_is_sparse should only be called on sparse.SparseResult or
tensor.
Tensor, not,"
,
x
)
return
isinstance
(
x
,
base_tensor
.
Base
Tensor
)
return
isinstance
(
x
,
tensor
.
Tensor
)
def
_is_sparse
(
x
):
def
_is_sparse
(
x
):
"""
"""
...
...
tensor.py
浏览文件 @
cb823769
...
@@ -4,11 +4,12 @@ import inspect
...
@@ -4,11 +4,12 @@ import inspect
import
numpy
import
numpy
from
copy
import
copy
from
gof
import
Result
,
Op
,
utils
,
Destroyer
,
Viewer
,
AbstractFunctionError
from
gof
import
Result
,
Op
,
utils
,
Destroyer
,
Viewer
,
AbstractFunctionError
import
gof.result
import
gof.result
import
gof.op
import
gof.op
from
base_tensor
import
BaseTensor
,
BaseTensorOp
import
blas
# for gemm, dot
import
blas
# for gemm, dot
import
elemwise
as
s2t
import
elemwise
as
s2t
...
@@ -17,16 +18,239 @@ import scalar as scal
...
@@ -17,16 +18,239 @@ import scalar as scal
from
functools
import
partial
from
functools
import
partial
class
Tensor
(
BaseTensor
):
class
Tensor
(
Result
):
"""
"""
This subclass of L{BaseTensor} provides operator overloading using
L{Result} to store L{numpy.ndarray} or equivalent via .data
implementations of L{Tensor} operations contained in this file.
This class does not implement python operators and has no dependencies
on the L{Op}s that use it.
@todo: At some point we should document a glossary, such as terms like
broadcasting and shape.
Operators:
@type _dtype: numpy dtype string such as 'int64' or 'float64' (among others)
- most numeric operators are overloaded (to return L{Op}s that
@type _broadcastable: tuple or list or array of boolean values, whose length
perform the corresponding calculation)
is the number of dimensions of the contained L{ndarray}.
@ivar _broadcastable: Each element of the broadcastable vector tells us
something about the corresponding dimension:
- False means the dimension can be anything.
- True means the dimension must be 1. Also, this dimension will be considered
for L{broadcasting}, as described and implemented in Numpy.
"""
"""
def
__init__
(
self
,
dtype
,
broadcastable
,
name
=
None
):
"""Initialize a L{Tensor}
@note: This does not actually allocate any data.
"""
# 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.
Result
.
__init__
(
self
,
role
=
None
,
name
=
name
)
self
.
_dtype
=
str
(
dtype
)
self
.
dtype_specs
()
# this is just for error checking
self
.
_broadcastable
=
tuple
(
broadcastable
)
######################
# Result interface
######################
#
# filter
#
def
filter
(
self
,
arr
):
"""Cast to an L{numpy.ndarray} and ensure arr has correct rank and shape."""
if
not
(
isinstance
(
arr
,
numpy
.
ndarray
)
\
and
arr
.
dtype
==
self
.
dtype
):
arr
=
numpy
.
asarray
(
arr
,
dtype
=
self
.
dtype
)
if
len
(
self
.
broadcastable
)
!=
len
(
arr
.
shape
):
raise
ValueError
(
Tensor
.
filter
.
E_rank
,
self
.
broadcastable
,
arr
.
shape
,
self
.
owner
)
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
#
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
L{self.dtype}. It is for use in C code generation.
"""
#TODO: add more type correspondances for e.g. int32, int64, float32,
#complex64, etc.
try
:
return
{
'float32'
:
(
float
,
'npy_float32'
,
'NPY_FLOAT32'
),
'float64'
:
(
float
,
'npy_float64'
,
'NPY_FLOAT64'
),
'int8'
:
(
int
,
'npy_int8'
,
'NPY_INT8'
),
'int16'
:
(
int
,
'npy_int16'
,
'NPY_INT16'
),
'int32'
:
(
int
,
'npy_int32'
,
'NPY_INT32'
),
'int64'
:
(
int
,
'npy_int64'
,
'NPY_INT64'
),
'complex128'
:
(
complex
,
'theano_complex128'
,
'NPY_COMPLEX128'
),
'complex64'
:
(
complex
,
'theano_complex64'
,
'NPY_COMPLEX64'
)}[
self
.
dtype
]
except
KeyError
:
raise
TypeError
(
"Unsupported dtype for
%
s:
%
s"
%
(
self
.
__class__
.
__name__
,
self
.
dtype
))
#
# Description for constant folding
#
def
desc
(
self
):
"""
Returns a hashable description of this L{Tensor}.
"""
if
self
.
data
is
not
None
:
return
(
Tensor
,
self
.
dtype
,
self
.
broadcastable
,
self
.
data
.
data
[:])
else
:
return
(
Tensor
,
self
.
dtype
,
self
.
broadcastable
,
None
)
#
# C codegen stubs
#
def
c_declare
(
self
,
name
,
sub
):
return
"""
PyArrayObject*
%(name)
s;
int type_num_
%(name)
s;
typedef
%(dtype)
s dtype_
%(name)
s;
"""
%
dict
(
sub
,
name
=
name
,
dtype
=
self
.
dtype_specs
()[
1
])
def
c_init
(
self
,
name
,
sub
):
return
"""
%(name)
s = NULL;
type_num_
%(name)
s =
%(type_num)
s;
"""
%
dict
(
sub
,
name
=
name
,
type_num
=
self
.
dtype_specs
()[
2
])
def
c_extract
(
self
,
name
,
sub
):
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
(
sub
,
name
=
name
,
type_num
=
self
.
dtype_specs
()[
2
])
def
c_cleanup
(
self
,
name
,
sub
):
return
"""
if (
%(name)
s) {
Py_XDECREF(
%(name)
s);
}
"""
%
locals
()
def
c_sync
(
self
,
name
,
sub
):
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);
}
"""
%
locals
()
def
c_headers
(
self
):
return
[]
def
c_libraries
(
self
):
return
[]
def
c_support_code
(
cls
):
template
=
"""
struct theano_complex
%(nbits)
s : public npy_complex
%(nbits)
s
{
typedef theano_complex
%(nbits)
s complex_type;
typedef npy_float
%(half_nbits)
s scalar_type;
complex_type operator +(complex_type y) {
complex_type ret;
ret.real = this->real + y.real;
ret.imag = this->imag + y.imag;
return ret;
}
complex_type operator -(complex_type y) {
complex_type ret;
ret.real = this->real - y.real;
ret.imag = this->imag - y.imag;
return ret;
}
complex_type operator *(complex_type y) {
complex_type ret;
ret.real = this->real * y.real - this->imag * y.imag;
ret.imag = this->real * y.imag + this->imag * y.real;
return ret;
}
complex_type operator /(complex_type y) {
complex_type ret;
scalar_type y_norm_square = y.real * y.real + y.imag * y.imag;
ret.real = (this->real * y.real + this->imag * y.imag) / y_norm_square;
ret.imag = (this->imag * y.real - this->real * y.imag) / y_norm_square;
return ret;
}
};
"""
return
template
%
dict
(
nbits
=
64
,
half_nbits
=
32
)
+
template
%
dict
(
nbits
=
128
,
half_nbits
=
64
)
# todo: use C templating
############################
# Tensor specific attributes
############################
dtype
=
property
(
lambda
self
:
self
.
_dtype
,
doc
=
"read-only access to _dtype, which should not be changed"
)
broadcastable
=
property
(
lambda
self
:
self
.
_broadcastable
,
doc
=
"read-only access to _broadcastable, which should not be changed"
)
############################
# Cloning facilities
############################
def
__copy__
(
self
):
return
self
.
clone
(
True
)
def
clone
(
self
,
transfer_data
=
False
):
"""Return a copy of this instance (with its own attributes)
If transfer_data is True, a copy of self.data is assigned to the copy's
data property, otherwise the copy's data is left as None.
"""
cpy
=
self
.
__class__
(
self
.
dtype
,
self
.
broadcastable
,
self
.
name
)
if
transfer_data
:
cpy
.
data
=
copy
(
self
.
data
)
return
cpy
#UNARY
#UNARY
def
__abs__
(
self
):
return
Abs
(
self
)
.
out
def
__abs__
(
self
):
return
Abs
(
self
)
.
out
def
__neg__
(
self
):
return
Neg
(
self
)
.
out
def
__neg__
(
self
):
return
Neg
(
self
)
.
out
...
@@ -79,7 +303,7 @@ s2t.Tensor = Tensor
...
@@ -79,7 +303,7 @@ s2t.Tensor = Tensor
# alternate Tensor constructor
# alternate Tensor constructor
def
astensor
(
data
,
broadcastable
=
None
,
name
=
None
):
def
astensor
(
data
,
broadcastable
=
None
,
name
=
None
):
"""Return a L{Tensor} containing given data"""
"""Return a L{Tensor} containing given data"""
if
isinstance
(
data
,
Base
Tensor
):
if
isinstance
(
data
,
Tensor
):
if
broadcastable
is
not
None
and
list
(
data
.
broadcastable
)
!=
list
(
broadcastable
):
if
broadcastable
is
not
None
and
list
(
data
.
broadcastable
)
!=
list
(
broadcastable
):
raise
TypeError
(
"The data to wrap as a Tensor has the wrong broadcastable pattern. Expected
%
s, got
%
s."
%
(
broadcastable
,
data
.
broadcastable
))
raise
TypeError
(
"The data to wrap as a Tensor has the wrong broadcastable pattern. Expected
%
s, got
%
s."
%
(
broadcastable
,
data
.
broadcastable
))
if
name
is
not
None
and
name
!=
data
.
name
:
if
name
is
not
None
and
name
!=
data
.
name
:
...
@@ -153,36 +377,57 @@ cols, icols, fcols = _multi(col, icol, fcol)
...
@@ -153,36 +377,57 @@ cols, icols, fcols = _multi(col, icol, fcol)
# to upcast their arguments... this internal-use function is a good place to put debugging stuff, better than the global astensor.
# to upcast their arguments... this internal-use function is a good place to put debugging stuff, better than the global astensor.
_as_tensor
=
astensor
_as_tensor
=
astensor
class
_Op
(
BaseTensorOp
):
"""A convenient base for the ops in this file"""
out_tensor_class
=
Tensor
@classmethod
class
_Op
(
Op
):
def
input_wrapper
(
cls
,
obj
):
"""
return
_as_tensor
(
obj
)
A basic L{Op} subclass that can be used to make L{Op}s that operate on L{Tensor}s.
It is not mandatory to inherit from this class, but it is practical.
def
c_var_names
(
self
):
(
self
,
inames
,
onames
),
_1
,
_2
,
_3
=
inspect
.
getargspec
(
self
.
c_impl
)
@ivar nin: number of inputs
inames
=
utils
.
from_return_values
(
inames
)
@ivar nout: number of outputs
onames
=
utils
.
from_return_values
(
onames
)
@ivar out_tensor_class: L{Tensor} subclass used to instantiate the outputs
return
[
inames
,
onames
]
- input_wrapper: returns a L{Tensor} from its argument
- propagate_dtype: returns a list of dtypes corresponding to the
output dtypes from a list of input dtypes (if an input is not a
L{Tensor}, the passed value will be None)
- propagate_broadcastable: returns a list of tuples corresponding
to the output broadcastable flags from the input broadcastable flags
(if an input is not a L{Tensor}, the passed value will be None).
"""
def
c_code
(
self
,
input_names
,
output_names
,
sub
):
nin
=
-
1
# nin == -1 means: arbitrary number of inputs
sub
=
dict
(
sub
)
nout
=
1
icvn
,
ocvn
=
self
.
c_var_names
()
for
real
,
tosub
in
zip
(
input_names
+
output_names
,
icvn
+
ocvn
):
def
__init__
(
self
,
*
inputs
):
sub
[
tosub
]
=
real
inputs
=
map
(
_as_tensor
,
inputs
)
return
self
.
c_impl
(
self
.
inputs
,
self
.
outputs
)
%
sub
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
)
def
c_impl
(
self
,
inputs
,
outputs
):
i_broadcastables
=
[
getattr
(
input
,
'broadcastable'
,
None
)
for
input
in
inputs
]
raise
AbstractFunctionError
(
"No c_impl for
%
s"
%
self
.
__class__
.
__name__
)
i_dtypes
=
[
getattr
(
input
,
'dtype'
,
None
)
for
input
in
inputs
]
class
_Unary
:
o_broadcastables
=
utils
.
from_return_values
(
self
.
propagate_broadcastable
(
*
i_broadcastables
))
nin
=
1
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
):
rval
=
set
([
dtype
for
dtype
in
i_dtypes
if
dtype
is
not
None
])
if
len
(
rval
)
==
0
:
raise
ValueError
(
"Cannot infer the dtypes of the outputs with no Tensor inputs."
)
elif
len
(
rval
)
>
1
:
raise
ValueError
(
"The dtypes of all inputs should be identical."
)
return
[
rval
.
pop
()]
*
self
.
nout
class
_Binary
:
nin
=
2
##########################
##########################
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论