Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
P
pytensor
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
testgroup
pytensor
Commits
74324316
提交
74324316
authored
2月 22, 2012
作者:
lamblin
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #469 from nouiz/remove_warning
Remove warning
上级
5f5f63e1
bdb5144c
隐藏空白字符变更
内嵌
并排
正在显示
2 个修改的文件
包含
337 行增加
和
205 行删除
+337
-205
debugmode.py
theano/compile/debugmode.py
+336
-205
__init__.py
theano/sandbox/cuda/__init__.py
+1
-0
没有找到文件。
theano/compile/debugmode.py
浏览文件 @
74324316
...
@@ -48,19 +48,22 @@ AddConfigVar('DebugMode.check_finite',
...
@@ -48,19 +48,22 @@ AddConfigVar('DebugMode.check_finite',
AddConfigVar
(
'DebugMode.check_strides'
,
AddConfigVar
(
'DebugMode.check_strides'
,
(
"Check that Python- and C-produced ndarrays have same strides. "
(
"Check that Python- and C-produced ndarrays have same strides. "
"On difference: (0) - ignore, (1) warn, or (2) raise error"
),
"On difference: (0) - ignore, (1) warn, or (2) raise error"
),
IntParam
(
1
,
lambda
i
:
i
in
(
0
,
1
,
2
)),
IntParam
(
1
,
lambda
i
:
i
in
(
0
,
1
,
2
)),
in_c_key
=
False
)
in_c_key
=
False
)
AddConfigVar
(
'DebugMode.warn_input_not_reused'
,
AddConfigVar
(
'DebugMode.warn_input_not_reused'
,
(
"Generate a warning when the destroy_map or view_map tell that an op work inplace, but the op did not reuse the input for its output."
(
"Generate a warning when the destroy_map or view_map tell that an"
" op work inplace, but the op did not reuse the input for its output."
),
),
BoolParam
(
True
),
BoolParam
(
True
),
in_c_key
=
False
)
in_c_key
=
False
)
def
is_valid_check_preallocated_output_param
(
param
):
def
is_valid_check_preallocated_output_param
(
param
):
if
not
isinstance
(
param
,
basestring
):
if
not
isinstance
(
param
,
basestring
):
return
False
return
False
valid
=
[
"previous"
,
"c_contiguous"
,
"f_contiguous"
,
"neg_strides"
,
"ALL"
,
""
]
valid
=
[
"previous"
,
"c_contiguous"
,
"f_contiguous"
,
"neg_strides"
,
"ALL"
,
""
]
for
p
in
param
.
split
(
":"
):
for
p
in
param
.
split
(
":"
):
if
p
not
in
valid
:
if
p
not
in
valid
:
return
False
return
False
...
@@ -77,12 +80,14 @@ AddConfigVar('DebugMode.check_preallocated_output',
...
@@ -77,12 +80,14 @@ AddConfigVar('DebugMode.check_preallocated_output',
in_c_key
=
False
)
in_c_key
=
False
)
import
logging
import
logging
_logger
=
logging
.
getLogger
(
"theano.compile.debugmode"
)
_logger
=
logging
.
getLogger
(
"theano.compile.debugmode"
)
_logger
.
setLevel
(
logging
.
WARNING
)
_logger
.
setLevel
(
logging
.
WARNING
)
# Filter to avoid duplicating optimization warnings
# Filter to avoid duplicating optimization warnings
class
NoDuplicateOptWarningFilter
(
logging
.
Filter
):
class
NoDuplicateOptWarningFilter
(
logging
.
Filter
):
prev_msgs
=
set
([])
prev_msgs
=
set
([])
def
filter
(
self
,
record
):
def
filter
(
self
,
record
):
msg
=
record
.
getMessage
()
msg
=
record
.
getMessage
()
if
msg
.
startswith
(
'Optimization Warning: '
):
if
msg
.
startswith
(
'Optimization Warning: '
):
...
@@ -95,16 +100,17 @@ class NoDuplicateOptWarningFilter(logging.Filter):
...
@@ -95,16 +100,17 @@ class NoDuplicateOptWarningFilter(logging.Filter):
_logger
.
addFilter
(
NoDuplicateOptWarningFilter
())
_logger
.
addFilter
(
NoDuplicateOptWarningFilter
())
########################
########################
#
#
# Exceptions
# Exceptions
#
#
########################
########################
class
DebugModeError
(
Exception
):
class
DebugModeError
(
Exception
):
"""Generic Exception raised to indicate an internal theano problem"""
"""Generic Exception raised to indicate an internal theano problem"""
pass
pass
class
BadCLinkerOutput
(
DebugModeError
):
class
BadCLinkerOutput
(
DebugModeError
):
"""Exception: an Op's c_code and perform implementations don't agree."""
"""Exception: an Op's c_code and perform implementations don't agree."""
...
@@ -119,20 +125,22 @@ class BadCLinkerOutput(DebugModeError):
...
@@ -119,20 +125,22 @@ class BadCLinkerOutput(DebugModeError):
def
__init__
(
self
,
r
,
val_py
,
val_c
):
def
__init__
(
self
,
r
,
val_py
,
val_c
):
"""Initialize members"""
"""Initialize members"""
DebugModeError
.
__init__
(
self
)
#
to be compatible with python2.4
DebugModeError
.
__init__
(
self
)
#
to be compatible with python2.4
self
.
r
=
r
self
.
r
=
r
self
.
val_py
=
val_py
self
.
val_py
=
val_py
self
.
val_c
=
val_c
self
.
val_c
=
val_c
def
offending_op
(
self
):
def
offending_op
(
self
):
"""Return the Op class whose c_code and perform implementations didn't match"""
"""Return the Op class whose c_code and perform
implementations didn't match"""
return
type
(
self
.
r
.
owner
.
op
)
return
type
(
self
.
r
.
owner
.
op
)
def
__str__
(
self
):
def
__str__
(
self
):
return
self
.
str_diagnostic
()
return
self
.
str_diagnostic
()
def
str_diagnostic
(
self
):
def
str_diagnostic
(
self
):
"""Return a pretty multiline string representating the cause of the exception"""
"""Return a pretty multiline string representating the cause
of the exception"""
sio
=
StringIO
()
sio
=
StringIO
()
print
>>
sio
,
"BadCLinkerOutput"
print
>>
sio
,
"BadCLinkerOutput"
print
>>
sio
,
" variable:"
,
self
.
r
print
>>
sio
,
" variable:"
,
self
.
r
...
@@ -171,15 +179,16 @@ class BadCLinkerOutput(DebugModeError):
...
@@ -171,15 +179,16 @@ class BadCLinkerOutput(DebugModeError):
except
Exception
:
except
Exception
:
pass
pass
try
:
try
:
ov
=
numpy
.
asarray
(
self
.
val_c
)
ov
=
numpy
.
asarray
(
self
.
val_c
)
nv
=
numpy
.
asarray
(
self
.
val_py
)
nv
=
numpy
.
asarray
(
self
.
val_py
)
ssio
=
StringIO
()
ssio
=
StringIO
()
absdiff
=
numpy
.
absolute
(
nv
-
ov
)
absdiff
=
numpy
.
absolute
(
nv
-
ov
)
print
>>
ssio
,
" Max Abs Diff: "
,
numpy
.
max
(
absdiff
)
print
>>
ssio
,
" Max Abs Diff: "
,
numpy
.
max
(
absdiff
)
print
>>
ssio
,
" Mean Abs Diff: "
,
numpy
.
mean
(
absdiff
)
print
>>
ssio
,
" Mean Abs Diff: "
,
numpy
.
mean
(
absdiff
)
print
>>
ssio
,
" Median Abs Diff: "
,
numpy
.
median
(
absdiff
)
print
>>
ssio
,
" Median Abs Diff: "
,
numpy
.
median
(
absdiff
)
print
>>
ssio
,
" Std Abs Diff: "
,
numpy
.
std
(
absdiff
)
print
>>
ssio
,
" Std Abs Diff: "
,
numpy
.
std
(
absdiff
)
reldiff
=
numpy
.
absolute
(
nv
-
ov
)
/
(
numpy
.
absolute
(
nv
)
+
numpy
.
absolute
(
ov
))
reldiff
=
numpy
.
absolute
(
nv
-
ov
)
/
(
numpy
.
absolute
(
nv
)
+
numpy
.
absolute
(
ov
))
print
>>
ssio
,
" Max Rel Diff: "
,
numpy
.
max
(
reldiff
)
print
>>
ssio
,
" Max Rel Diff: "
,
numpy
.
max
(
reldiff
)
print
>>
ssio
,
" Mean Rel Diff: "
,
numpy
.
mean
(
reldiff
)
print
>>
ssio
,
" Mean Rel Diff: "
,
numpy
.
mean
(
reldiff
)
print
>>
ssio
,
" Median Rel Diff: "
,
numpy
.
median
(
reldiff
)
print
>>
ssio
,
" Median Rel Diff: "
,
numpy
.
median
(
reldiff
)
...
@@ -190,12 +199,15 @@ class BadCLinkerOutput(DebugModeError):
...
@@ -190,12 +199,15 @@ class BadCLinkerOutput(DebugModeError):
pass
pass
return
sio
.
getvalue
()
return
sio
.
getvalue
()
class
BadOptimization
(
DebugModeError
):
class
BadOptimization
(
DebugModeError
):
"""Exception: some variable and its substitute take different runtime values.
"""Exception: some variable and its substitute take different
runtime values.
"""
"""
new_r
=
None
new_r
=
None
"""A `Variable` instance that took a different value from `old_r`, but which replaced `old_r`."""
"""A `Variable` instance that took a different value from `old_r`,
but which replaced `old_r`."""
old_r
=
None
old_r
=
None
"""A `Variable` instance that was replaced by `new_r`."""
"""A `Variable` instance that was replaced by `new_r`."""
...
@@ -209,18 +221,22 @@ class BadOptimization(DebugModeError):
...
@@ -209,18 +221,22 @@ class BadOptimization(DebugModeError):
reason
=
None
reason
=
None
"""An object that indicates why old_r was turned into new_r.
"""An object that indicates why old_r was turned into new_r.
Convention is that this is the name of the optimization that requested the replacement.
Convention is that this is the name of the optimization that
requested the replacement.
"""
"""
old_graph
=
""
old_graph
=
""
"""A multiline string representation of the graph leading to old_r, at the time of the replacement."""
"""A multiline string representation of the graph leading to
old_r, at the time of the replacement."""
new_graph
=
""
new_graph
=
""
"""A multiline string representation of the graph leading to new_r, at the time of the replacement."""
"""A multiline string representation of the graph leading to
new_r, at the time of the replacement."""
def
__init__
(
self
,
old_r
,
new_r
,
old_r_val
,
new_r_val
,
reason
,
old_graph
,
new_graph
):
def
__init__
(
self
,
old_r
,
new_r
,
old_r_val
,
new_r_val
,
reason
,
old_graph
,
new_graph
):
"""Initialize members"""
"""Initialize members"""
DebugModeError
.
__init__
(
self
)
#
to be compatible with python2.4
DebugModeError
.
__init__
(
self
)
#
to be compatible with python2.4
self
.
old_r
=
old_r
self
.
old_r
=
old_r
self
.
new_r
=
new_r
self
.
new_r
=
new_r
self
.
old_r_val
=
old_r_val
self
.
old_r_val
=
old_r_val
...
@@ -233,10 +249,12 @@ class BadOptimization(DebugModeError):
...
@@ -233,10 +249,12 @@ class BadOptimization(DebugModeError):
return
self
.
str_diagnostic
()
return
self
.
str_diagnostic
()
def
str_diagnostic
(
self
):
def
str_diagnostic
(
self
):
"""Return a pretty multiline string representating the cause of the exception"""
"""Return a pretty multiline string representating the cause
of the exception"""
sio
=
StringIO
()
sio
=
StringIO
()
val_str_len_limit
=
800
val_str_len_limit
=
800
print
>>
sio
,
"BadOptimization Error"
,
super
(
BadOptimization
,
self
)
.
__str__
()
print
>>
sio
,
"BadOptimization Error"
,
super
(
BadOptimization
,
self
)
.
__str__
()
print
>>
sio
,
" Variable: id"
,
id
(
self
.
new_r
),
self
.
new_r
print
>>
sio
,
" Variable: id"
,
id
(
self
.
new_r
),
self
.
new_r
print
>>
sio
,
" Op"
,
self
.
new_r
.
owner
print
>>
sio
,
" Op"
,
self
.
new_r
.
owner
print
>>
sio
,
" Value Type:"
,
type
(
self
.
new_r_val
)
print
>>
sio
,
" Value Type:"
,
type
(
self
.
new_r_val
)
...
@@ -253,7 +271,8 @@ class BadOptimization(DebugModeError):
...
@@ -253,7 +271,8 @@ class BadOptimization(DebugModeError):
str_old_r_val
=
str
(
self
.
old_r_val
)
str_old_r_val
=
str
(
self
.
old_r_val
)
if
len
(
str_old_r_val
)
>
val_str_len_limit
:
if
len
(
str_old_r_val
)
>
val_str_len_limit
:
print
>>
sio
,
" Old Value: "
,
str
(
self
.
old_r_val
)[:
val_str_len_limit
],
'...'
print
>>
sio
,
" Old Value: "
,
str
(
self
.
old_r_val
)[
:
val_str_len_limit
],
'...'
else
:
else
:
print
>>
sio
,
" Old Value: "
,
str
(
self
.
old_r_val
)
print
>>
sio
,
" Old Value: "
,
str
(
self
.
old_r_val
)
...
@@ -269,18 +288,23 @@ class BadOptimization(DebugModeError):
...
@@ -269,18 +288,23 @@ class BadOptimization(DebugModeError):
pass
pass
str_new_r_val
=
str
(
self
.
new_r_val
)
str_new_r_val
=
str
(
self
.
new_r_val
)
if
len
(
str_new_r_val
)
>
val_str_len_limit
:
if
len
(
str_new_r_val
)
>
val_str_len_limit
:
print
>>
sio
,
" New Value: "
,
str
(
self
.
new_r_val
)[:
val_str_len_limit
],
'...'
print
>>
sio
,
" New Value: "
,
str
(
self
.
new_r_val
)[
:
val_str_len_limit
],
'...'
else
:
else
:
print
>>
sio
,
" New Value: "
,
str
(
self
.
new_r_val
)
print
>>
sio
,
" New Value: "
,
str
(
self
.
new_r_val
)
try
:
try
:
ov
=
numpy
.
asarray
(
self
.
old_r_val
)
ov
=
numpy
.
asarray
(
self
.
old_r_val
)
nv
=
numpy
.
asarray
(
self
.
new_r_val
)
nv
=
numpy
.
asarray
(
self
.
new_r_val
)
ssio
=
StringIO
()
ssio
=
StringIO
()
print
>>
ssio
,
" Max Abs Diff: "
,
numpy
.
max
(
numpy
.
absolute
(
nv
-
ov
))
print
>>
ssio
,
" Max Abs Diff: "
,
numpy
.
max
(
numpy
.
absolute
(
nv
-
print
>>
ssio
,
" Mean Abs Diff: "
,
numpy
.
mean
(
numpy
.
absolute
(
nv
-
ov
))
ov
))
print
>>
ssio
,
" Median Abs Diff: "
,
numpy
.
median
(
numpy
.
absolute
(
nv
-
ov
))
print
>>
ssio
,
" Mean Abs Diff: "
,
numpy
.
mean
(
numpy
.
absolute
(
nv
-
print
>>
ssio
,
" Std Abs Diff: "
,
numpy
.
std
(
numpy
.
absolute
(
nv
-
ov
))
ov
))
print
>>
ssio
,
" Median Abs Diff: "
,
numpy
.
median
(
numpy
.
absolute
(
nv
-
ov
))
print
>>
ssio
,
" Std Abs Diff: "
,
numpy
.
std
(
numpy
.
absolute
(
nv
-
ov
))
# N.B. the maximum(..., 1e-8) protects against div by 0 when
# N.B. the maximum(..., 1e-8) protects against div by 0 when
# nv == ov == 0
# nv == ov == 0
...
@@ -307,8 +331,10 @@ class BadOptimization(DebugModeError):
...
@@ -307,8 +331,10 @@ class BadOptimization(DebugModeError):
print
>>
sio
,
" or even tensor.cmp_sloppy=2 for less-strict comparison"
print
>>
sio
,
" or even tensor.cmp_sloppy=2 for less-strict comparison"
return
sio
.
getvalue
()
return
sio
.
getvalue
()
class
BadDestroyMap
(
DebugModeError
):
class
BadDestroyMap
(
DebugModeError
):
"""Exception: Some perform() or c_code() modified an input that wasn't in the destroy_map"""
"""Exception: Some perform() or c_code() modified an input that
wasn't in the destroy_map"""
def
__init__
(
self
,
node
,
idx
,
old_val
,
new_val
,
perform
):
def
__init__
(
self
,
node
,
idx
,
old_val
,
new_val
,
perform
):
#super(BadDestroyMap, self).__init__()
#super(BadDestroyMap, self).__init__()
DebugModeError
.
__init__
(
self
)
#to be compatible with python2.4
DebugModeError
.
__init__
(
self
)
#to be compatible with python2.4
...
@@ -322,8 +348,10 @@ class BadDestroyMap(DebugModeError):
...
@@ -322,8 +348,10 @@ class BadDestroyMap(DebugModeError):
sio
=
StringIO
()
sio
=
StringIO
()
print
>>
sio
,
" node:"
,
self
.
node
print
>>
sio
,
" node:"
,
self
.
node
print
>>
sio
,
" perform:"
,
self
.
perform
print
>>
sio
,
" perform:"
,
self
.
perform
print
>>
sio
,
" node.inputs:"
,
[(
str
(
i
),
id
(
i
))
for
i
in
self
.
node
.
inputs
]
print
>>
sio
,
" node.inputs:"
,
[(
str
(
i
),
id
(
i
))
print
>>
sio
,
" destroy_map:"
,
getattr
(
self
.
node
.
op
,
'destroy_map'
,
{})
for
i
in
self
.
node
.
inputs
]
print
>>
sio
,
" destroy_map:"
,
getattr
(
self
.
node
.
op
,
'destroy_map'
,
{})
print
>>
sio
,
" changed input idx:"
,
self
.
idx
print
>>
sio
,
" changed input idx:"
,
self
.
idx
print
>>
sio
,
" changed input type:"
,
self
.
node
.
inputs
[
self
.
idx
]
.
type
print
>>
sio
,
" changed input type:"
,
self
.
node
.
inputs
[
self
.
idx
]
.
type
print
>>
sio
,
" repr (old val):"
,
repr
(
self
.
old_val
)
print
>>
sio
,
" repr (old val):"
,
repr
(
self
.
old_val
)
...
@@ -347,11 +375,14 @@ class BadDestroyMap(DebugModeError):
...
@@ -347,11 +375,14 @@ class BadDestroyMap(DebugModeError):
print
>>
sio
,
" Hint: this can also be caused by a deficient values_eq_approx() or __eq__() implementation [which compared input values]"
print
>>
sio
,
" Hint: this can also be caused by a deficient values_eq_approx() or __eq__() implementation [which compared input values]"
return
sio
.
getvalue
()
return
sio
.
getvalue
()
class
BadViewMap
(
DebugModeError
):
class
BadViewMap
(
DebugModeError
):
"""Exception: Some perform() or c_code() created a memory alias that wasn't in the view_map"""
"""Exception: Some perform() or c_code() created a memory alias
def
__init__
(
self
,
node
,
output_idx
,
out_storage
,
in_alias_idx
=
None
,
out_alias_idx
=
None
):
that wasn't in the view_map"""
def
__init__
(
self
,
node
,
output_idx
,
out_storage
,
in_alias_idx
=
None
,
out_alias_idx
=
None
):
#super(BadViewMap, self).__init__()
#super(BadViewMap, self).__init__()
DebugModeError
.
__init__
(
self
)
#
to be compatible with python2.4
DebugModeError
.
__init__
(
self
)
#
to be compatible with python2.4
self
.
node
=
node
self
.
node
=
node
self
.
output_idx
=
output_idx
self
.
output_idx
=
output_idx
self
.
out_storage
=
out_storage
self
.
out_storage
=
out_storage
...
@@ -361,10 +392,13 @@ class BadViewMap(DebugModeError):
...
@@ -361,10 +392,13 @@ class BadViewMap(DebugModeError):
def
__str__
(
self
):
def
__str__
(
self
):
sio
=
StringIO
()
sio
=
StringIO
()
print
>>
sio
,
" node:"
,
self
.
node
print
>>
sio
,
" node:"
,
self
.
node
print
>>
sio
,
" node.inputs:"
,
[(
str
(
i
),
id
(
i
))
for
i
in
self
.
node
.
inputs
]
print
>>
sio
,
" node.inputs:"
,
[(
str
(
i
),
id
(
i
))
print
>>
sio
,
" node.outputs:"
,
[(
str
(
i
),
id
(
i
))
for
i
in
self
.
node
.
outputs
]
for
i
in
self
.
node
.
inputs
]
print
>>
sio
,
" node.outputs:"
,
[(
str
(
i
),
id
(
i
))
for
i
in
self
.
node
.
outputs
]
print
>>
sio
,
" view_map:"
,
getattr
(
self
.
node
.
op
,
'view_map'
,
{})
print
>>
sio
,
" view_map:"
,
getattr
(
self
.
node
.
op
,
'view_map'
,
{})
print
>>
sio
,
" destroy_map:"
,
getattr
(
self
.
node
.
op
,
'destroy_map'
,
{})
print
>>
sio
,
" destroy_map:"
,
getattr
(
self
.
node
.
op
,
'destroy_map'
,
{})
print
>>
sio
,
" aliased output:"
,
self
.
output_idx
print
>>
sio
,
" aliased output:"
,
self
.
output_idx
print
>>
sio
,
" aliased output storage:"
,
self
.
out_storage
print
>>
sio
,
" aliased output storage:"
,
self
.
out_storage
if
self
.
in_alias_idx
:
if
self
.
in_alias_idx
:
...
@@ -373,27 +407,33 @@ class BadViewMap(DebugModeError):
...
@@ -373,27 +407,33 @@ class BadViewMap(DebugModeError):
print
>>
sio
,
" aliased to outputs:"
,
self
.
out_alias_idx
print
>>
sio
,
" aliased to outputs:"
,
self
.
out_alias_idx
return
sio
.
getvalue
()
return
sio
.
getvalue
()
class
StochasticOrder
(
DebugModeError
):
class
StochasticOrder
(
DebugModeError
):
"""Exception: Repeated Optimizations of the same graph do not give identical results.
"""Exception: Repeated Optimizations of the same graph do not give
identical results.
The most common cause is that an Optimization iterates over some objects in a
The most common cause is that an Optimization iterates over some
memory-address-dependent order (such as id() or object.hash()). If you see this error and
objects in a memory-address-dependent order (such as id() or
you think it is related to optimizations within Theano, email theano-dev with the message
object.hash()). If you see this error and you think it is related
to optimizations within Theano, email theano-dev with the message
attached to this exception.
attached to this exception.
"""
"""
pass
pass
class
InvalidValueError
(
DebugModeError
):
class
InvalidValueError
(
DebugModeError
):
"""Exception: some Op an output value that is inconsistent with the Type of that output"""
"""Exception: some Op an output value that is inconsistent with
def
__init__
(
self
,
r
,
v
,
client_node
=
None
,
hint
=
'none'
,
specific_hint
=
'none'
):
the Type of that output"""
def
__init__
(
self
,
r
,
v
,
client_node
=
None
,
hint
=
'none'
,
specific_hint
=
'none'
):
#super(InvalidValueError, self).__init__()
#super(InvalidValueError, self).__init__()
DebugModeError
.
__init__
(
self
)
#
to be compatible with python2.4
DebugModeError
.
__init__
(
self
)
#
to be compatible with python2.4
self
.
r
=
r
self
.
r
=
r
self
.
v
=
v
self
.
v
=
v
self
.
client_node
=
client_node
self
.
client_node
=
client_node
self
.
hint
=
hint
self
.
hint
=
hint
self
.
specific_hint
=
specific_hint
self
.
specific_hint
=
specific_hint
def
__str__
(
self
):
def
__str__
(
self
):
r
,
v
=
self
.
r
,
self
.
v
r
,
v
=
self
.
r
,
self
.
v
...
@@ -455,7 +495,7 @@ def debugprint(r, prefix='', depth=-1, done=None, print_type=False,
...
@@ -455,7 +495,7 @@ def debugprint(r, prefix='', depth=-1, done=None, print_type=False,
:param print_view_map: wether to print the op view_map after ofther info
:param print_view_map: wether to print the op view_map after ofther info
:param order: If not empty will print the index in the toposort.
:param order: If not empty will print the index in the toposort.
"""
"""
if
depth
==
0
:
if
depth
==
0
:
return
return
if
done
is
None
:
if
done
is
None
:
...
@@ -478,40 +518,41 @@ def debugprint(r, prefix='', depth=-1, done=None, print_type=False,
...
@@ -478,40 +518,41 @@ def debugprint(r, prefix='', depth=-1, done=None, print_type=False,
r_name
=
''
r_name
=
''
if
print_destroy_map
:
if
print_destroy_map
:
destroy_map_str
=
str
(
getattr
(
r
.
owner
.
op
,
'destroy_map'
,
''
))
destroy_map_str
=
str
(
getattr
(
r
.
owner
.
op
,
'destroy_map'
,
''
))
else
:
else
:
destroy_map_str
=
''
destroy_map_str
=
''
if
print_view_map
:
if
print_view_map
:
view_map_str
=
str
(
getattr
(
r
.
owner
.
op
,
'view_map'
,
''
))
view_map_str
=
str
(
getattr
(
r
.
owner
.
op
,
'view_map'
,
''
))
else
:
else
:
view_map_str
=
''
view_map_str
=
''
if
destroy_map_str
and
destroy_map_str
!=
'{}'
:
if
destroy_map_str
and
destroy_map_str
!=
'{}'
:
destroy_map_str
=
'd='
+
destroy_map_str
destroy_map_str
=
'd='
+
destroy_map_str
if
view_map_str
and
view_map_str
!=
'{}'
:
if
view_map_str
and
view_map_str
!=
'{}'
:
view_map_str
=
'v='
+
view_map_str
view_map_str
=
'v='
+
view_map_str
o
=
''
o
=
''
if
order
:
if
order
:
o
=
str
(
order
.
index
(
r
.
owner
))
o
=
str
(
order
.
index
(
r
.
owner
))
if
len
(
a
.
outputs
)
==
1
:
if
len
(
a
.
outputs
)
==
1
:
print
>>
file
,
'
%
s
%
s [@
%
i]
%
s
\'
%
s
\'
%
s
%
s
%
s'
%
(
prefix
,
a
.
op
,
id
(
r
),
print
>>
file
,
'
%
s
%
s [@
%
i]
%
s
\'
%
s
\'
%
s
%
s
%
s'
%
(
prefix
,
a
.
op
,
type_str
,
r_name
,
id
(
r
),
destroy_map_str
,
type_str
,
r_name
,
view_map_str
,
o
)
else
:
print
>>
file
,
'
%
s
%
s.
%
i [@
%
i]
%
s
\'
%
s
\'
%
s
%
s
%
s'
%
(
prefix
,
a
.
op
,
a
.
outputs
.
index
(
r
),
id
(
r
),
type_str
,
r_name
,
destroy_map_str
,
destroy_map_str
,
view_map_str
,
view_map_str
,
o
)
o
)
else
:
print
>>
file
,
'
%
s
%
s.
%
i [@
%
i]
%
s
\'
%
s
\'
%
s
%
s
%
s'
%
(
prefix
,
a
.
op
,
a
.
outputs
.
index
(
r
),
id
(
r
),
type_str
,
r_name
,
destroy_map_str
,
view_map_str
,
o
)
if
id
(
a
)
not
in
done
:
if
id
(
a
)
not
in
done
:
done
.
add
(
id
(
a
))
done
.
add
(
id
(
a
))
for
i
in
a
.
inputs
:
for
i
in
a
.
inputs
:
debugprint
(
i
,
prefix
+
' |'
,
depth
=
depth
-
1
,
done
=
done
,
debugprint
(
i
,
prefix
+
' |'
,
depth
=
depth
-
1
,
done
=
done
,
print_type
=
print_type
,
file
=
file
,
order
=
order
)
print_type
=
print_type
,
file
=
file
,
order
=
order
)
else
:
else
:
#this is a variable
#this is a variable
...
@@ -519,7 +560,7 @@ def debugprint(r, prefix='', depth=-1, done=None, print_type=False,
...
@@ -519,7 +560,7 @@ def debugprint(r, prefix='', depth=-1, done=None, print_type=False,
return
file
return
file
def
_optcheck_env
(
input_specs
,
output_specs
,
accept_inplace
=
False
):
def
_optcheck_env
(
input_specs
,
output_specs
,
accept_inplace
=
False
):
"""Create an Env for debugging.
"""Create an Env for debugging.
:param input_specs: env inputs
:param input_specs: env inputs
...
@@ -547,18 +588,23 @@ def _optcheck_env(input_specs, output_specs, accept_inplace = False):
...
@@ -547,18 +588,23 @@ def _optcheck_env(input_specs, output_specs, accept_inplace = False):
if
not
accept_inplace
:
if
not
accept_inplace
:
for
node
in
env
.
nodes
:
for
node
in
env
.
nodes
:
if
getattr
(
node
.
op
,
'destroy_map'
,
None
):
if
getattr
(
node
.
op
,
'destroy_map'
,
None
):
raise
TypeError
(
"Graph must not contain inplace operations"
,
node
)
raise
TypeError
(
"Graph must not contain inplace operations"
,
node
)
# We need to protect all immutable inputs from inplace operations.
# We need to protect all immutable inputs from inplace operations.
env
.
extend
(
Supervisor
(
input
for
spec
,
input
in
zip
(
input_specs
,
inputs
)
if
not
(
spec
.
mutable
or
(
hasattr
(
env
,
'destroyers'
)
and
env
.
destroyers
(
input
)))))
env
.
extend
(
Supervisor
(
input
for
spec
,
input
in
zip
(
input_specs
,
inputs
)
if
not
(
spec
.
mutable
or
(
hasattr
(
env
,
'destroyers'
)
and
env
.
destroyers
(
input
)))))
# If named nodes are replaced, keep the name
# If named nodes are replaced, keep the name
env
.
extend
(
gof
.
toolbox
.
PreserveNames
())
env
.
extend
(
gof
.
toolbox
.
PreserveNames
())
return
env
,
map
(
SymbolicOutput
,
updates
),
equivalence_tracker
return
env
,
map
(
SymbolicOutput
,
updates
),
equivalence_tracker
def
_check_inputs
(
node
,
storage_map
,
r_vals
,
dr_vals
,
active_nodes
,
clobber_dr_vals
=
True
,
perform
=
None
,
warn_input_not_reused
=
True
):
def
_check_inputs
(
node
,
storage_map
,
r_vals
,
dr_vals
,
active_nodes
,
clobber_dr_vals
=
True
,
perform
=
None
,
warn_input_not_reused
=
True
):
"""Raise BadDestroyMap if necessary, update dr_vals"""
"""Raise BadDestroyMap if necessary, update dr_vals"""
destroyed_idx_list
=
[]
destroyed_idx_list
=
[]
destroy_map
=
getattr
(
node
.
op
,
'destroy_map'
,
{})
destroy_map
=
getattr
(
node
.
op
,
'destroy_map'
,
{})
...
@@ -567,11 +613,11 @@ def _check_inputs(node, storage_map, r_vals, dr_vals, active_nodes, clobber_dr_v
...
@@ -567,11 +613,11 @@ def _check_inputs(node, storage_map, r_vals, dr_vals, active_nodes, clobber_dr_v
destroyed_res_list
=
[
node
.
inputs
[
i
]
for
i
in
destroyed_idx_list
]
destroyed_res_list
=
[
node
.
inputs
[
i
]
for
i
in
destroyed_idx_list
]
if
warn_input_not_reused
and
destroyed_res_list
:
if
warn_input_not_reused
and
destroyed_res_list
:
dmap
=
getattr
(
node
.
op
,
'destroy_map'
,
{})
dmap
=
getattr
(
node
.
op
,
'destroy_map'
,
{})
for
oo
,
ii
in
dmap
.
iteritems
():
for
oo
,
ii
in
dmap
.
iteritems
():
out_var
=
storage_map
[
node
.
outputs
[
oo
]][
0
]
out_var
=
storage_map
[
node
.
outputs
[
oo
]][
0
]
in_var
=
storage_map
[
node
.
inputs
[
ii
[
0
]]][
0
]
in_var
=
storage_map
[
node
.
inputs
[
ii
[
0
]]][
0
]
if
isinstance
(
node
.
op
,
theano
.
compile
.
mode
.
OutputGuard
):
if
isinstance
(
node
.
op
,
theano
.
compile
.
mode
.
OutputGuard
):
# The point of OutputGuard is to be declared as destructive
# The point of OutputGuard is to be declared as destructive
# while not destroying anything
# while not destroying anything
continue
continue
...
@@ -581,15 +627,15 @@ def _check_inputs(node, storage_map, r_vals, dr_vals, active_nodes, clobber_dr_v
...
@@ -581,15 +627,15 @@ def _check_inputs(node, storage_map, r_vals, dr_vals, active_nodes, clobber_dr_v
ii
[
0
],
str
(
node
))
ii
[
0
],
str
(
node
))
if
warn_input_not_reused
:
if
warn_input_not_reused
:
vmap
=
getattr
(
node
.
op
,
'view_map'
,
{})
vmap
=
getattr
(
node
.
op
,
'view_map'
,
{})
for
oo
,
ii
in
vmap
.
iteritems
():
for
oo
,
ii
in
vmap
.
iteritems
():
out_var
=
storage_map
[
node
.
outputs
[
oo
]][
0
]
out_var
=
storage_map
[
node
.
outputs
[
oo
]][
0
]
in_var
=
storage_map
[
node
.
inputs
[
ii
[
0
]]][
0
]
in_var
=
storage_map
[
node
.
inputs
[
ii
[
0
]]][
0
]
# We don't try to optimize simple scalar and empty ndarray,
# We don't try to optimize simple scalar and empty ndarray,
# as this is not worth our time. This happen at least in
# as this is not worth our time. This happen at least in
# Subtensor when the output is a scalar But this depend on
# Subtensor when the output is a scalar But this depend on
# the version of numpy!
# the version of numpy!
if
getattr
(
out_var
,
'size'
,
2
)
<=
1
:
if
getattr
(
out_var
,
'size'
,
2
)
<=
1
:
continue
continue
if
isinstance
(
node
.
op
,
theano
.
compile
.
mode
.
OutputGuard
):
if
isinstance
(
node
.
op
,
theano
.
compile
.
mode
.
OutputGuard
):
# This class is not in the final graph.
# This class is not in the final graph.
...
@@ -613,7 +659,8 @@ def _check_inputs(node, storage_map, r_vals, dr_vals, active_nodes, clobber_dr_v
...
@@ -613,7 +659,8 @@ def _check_inputs(node, storage_map, r_vals, dr_vals, active_nodes, clobber_dr_v
dr_vals
[
r
]
=
(
storage_map
[
r
][
0
],
node
)
#no copy, this is the last use of this variable
dr_vals
[
r
]
=
(
storage_map
[
r
][
0
],
node
)
#no copy, this is the last use of this variable
storage_map
[
r
][
0
]
=
None
#make sure that dr_vals[r] doens't get used again
storage_map
[
r
][
0
]
=
None
#make sure that dr_vals[r] doens't get used again
else
:
else
:
raise
BadDestroyMap
(
node
,
r_idx
,
r_vals
[
r
],
storage_map
[
r
][
0
],
perform
)
raise
BadDestroyMap
(
node
,
r_idx
,
r_vals
[
r
],
storage_map
[
r
][
0
],
perform
)
def
_check_viewmap
(
node
,
storage_map
):
def
_check_viewmap
(
node
,
storage_map
):
...
@@ -641,8 +688,9 @@ def _check_viewmap(node, storage_map):
...
@@ -641,8 +688,9 @@ def _check_viewmap(node, storage_map):
view_map
=
getattr
(
node
.
op
,
'view_map'
,
{})
view_map
=
getattr
(
node
.
op
,
'view_map'
,
{})
destroy_map
=
getattr
(
node
.
op
,
'destroy_map'
,
{})
destroy_map
=
getattr
(
node
.
op
,
'destroy_map'
,
{})
# In theory, theano's view_map only allows for 1 output to alias 1 input
# In theory, theano's view_map only allows for 1 output to
# Checking for multiple aliases just in case...
# alias 1 input. Checking for multiple aliases just in
# case...
for
ii
,
inode
in
enumerate
(
node
.
inputs
):
for
ii
,
inode
in
enumerate
(
node
.
inputs
):
...
@@ -660,7 +708,7 @@ def _check_viewmap(node, storage_map):
...
@@ -660,7 +708,7 @@ def _check_viewmap(node, storage_map):
#TODO: make sure this is correct
#TODO: make sure this is correct
# According to OB, duplicate inputs are rejected on build graph time
# According to OB, duplicate inputs are rejected on build graph time
# if they cause problems. So if they are here it should be ok.
# if they cause problems. So if they are here it should be ok.
for
key
,
val
in
good_alias
.
iteritems
():
for
key
,
val
in
good_alias
.
iteritems
():
bad_alias
.
pop
(
key
,
None
)
bad_alias
.
pop
(
key
,
None
)
if
bad_alias
:
if
bad_alias
:
raise
BadViewMap
(
node
,
oi
,
outstorage
,
bad_alias
.
values
())
raise
BadViewMap
(
node
,
oi
,
outstorage
,
bad_alias
.
values
())
...
@@ -668,33 +716,39 @@ def _check_viewmap(node, storage_map):
...
@@ -668,33 +716,39 @@ def _check_viewmap(node, storage_map):
#if its not aliased to input, check output->output aliasing
#if its not aliased to input, check output->output aliasing
if
not
good_alias
and
_is_used_in_graph
(
onode
):
if
not
good_alias
and
_is_used_in_graph
(
onode
):
for
other_oi
,
other_onode
in
enumerate
(
node
.
outputs
):
for
other_oi
,
other_onode
in
enumerate
(
node
.
outputs
):
if
other_oi
==
oi
:
continue
if
other_oi
==
oi
:
continue
other_storage
=
storage_map
[
other_onode
][
0
]
other_storage
=
storage_map
[
other_onode
][
0
]
# check to see if we share memory with this other output
# check to see if we share memory with this other output
# this is not a problem if the node is not actually used
# this is not a problem if the node is not actually used
if
_is_used_in_graph
(
other_onode
)
and
\
if
_is_used_in_graph
(
other_onode
)
and
\
_may_share_memory
(
outstorage
,
other_storage
):
_may_share_memory
(
outstorage
,
other_storage
):
raise
BadViewMap
(
node
,
oi
,
outstorage
,
out_alias_idx
=
other_oi
)
raise
BadViewMap
(
node
,
oi
,
outstorage
,
out_alias_idx
=
other_oi
)
def
_may_share_memory
(
a
,
b
):
def
_may_share_memory
(
a
,
b
):
from
theano.misc.may_share_memory
import
may_share_memory
from
theano.misc.may_share_memory
import
may_share_memory
return
may_share_memory
(
a
,
b
,
False
)
return
may_share_memory
(
a
,
b
,
False
)
def
_is_function_output
(
node
):
def
_is_function_output
(
node
):
"""
"""
Returns True if the node in question is the a final output of the graph
Returns True if the node in question is the a final output of the graph
"""
"""
return
node
.
clients
==
[(
'output'
,
1
)]
return
node
.
clients
==
[(
'output'
,
1
)]
def
_is_used_in_graph
(
node
):
def
_is_used_in_graph
(
node
):
return
not
(
_is_function_output
(
node
)
or
node
.
clients
==
[])
return
not
(
_is_function_output
(
node
)
or
node
.
clients
==
[])
def
_check_strides_match
(
a
,
b
,
warn_err
,
op
):
def
_check_strides_match
(
a
,
b
,
warn_err
,
op
):
"""
"""
param: warn_err: if 0, no warning, if 1 warning, if 2 error
param: warn_err: if 0, no warning, if 1 warning, if 2 error
"""
"""
if
warn_err
==
0
:
return
if
warn_err
==
0
:
return
try
:
try
:
strides_eq
=
a
.
strides
==
b
.
strides
strides_eq
=
a
.
strides
==
b
.
strides
...
@@ -702,12 +756,14 @@ def _check_strides_match(a, b, warn_err, op):
...
@@ -702,12 +756,14 @@ def _check_strides_match(a, b, warn_err, op):
return
# no strides
return
# no strides
if
not
strides_eq
:
if
not
strides_eq
:
e
=
TypeError
(
'Stride mismatch'
,
(
a
.
shape
,
b
.
shape
,
a
.
strides
,
b
.
strides
,
str
(
op
)))
e
=
TypeError
(
'Stride mismatch'
,
(
a
.
shape
,
b
.
shape
,
a
.
strides
,
if
warn_err
==
2
:
b
.
strides
,
str
(
op
)))
if
warn_err
==
2
:
raise
e
raise
e
else
:
else
:
print
>>
sys
.
stderr
,
'WARNING:'
,
e
print
>>
sys
.
stderr
,
'WARNING:'
,
e
def
_lessbroken_deepcopy
(
a
):
def
_lessbroken_deepcopy
(
a
):
"""
"""
:param a: any object
:param a: any object
...
@@ -727,15 +783,19 @@ def _lessbroken_deepcopy(a):
...
@@ -727,15 +783,19 @@ def _lessbroken_deepcopy(a):
assert
rval
.
dtype
==
a
.
dtype
assert
rval
.
dtype
==
a
.
dtype
return
rval
return
rval
def
_find_bad_optimizations0
(
order
,
reasons
,
r_vals
):
def
_find_bad_optimizations0
(
order
,
reasons
,
r_vals
):
"""Use a simple algorithm to find broken optimizations.
"""Use a simple algorithm to find broken optimizations.
This algorithm is simple to understand, but sometimes when there's a problem it identifies
This algorithm is simple to understand, but sometimes when there's
the wrong optimization as the culprit. The problem stems from the fact that results are
a problem it identifies the wrong optimization as the culprit.
not evaluated in chronological order (looking at when they were introduced to the graph).
The problem stems from the fact that results are not evaluated in
chronological order (looking at when they were introduced to the
graph).
"""
"""
# iterate over variables looking for values that don't match the values of the
# iterate over variables looking for values that don't match the
# variables they replaced. This is the sign of a broken optimization.
# values of the variables they replaced. This is the sign of a
# broken optimization.
for
i
,
node
in
enumerate
(
order
):
for
i
,
node
in
enumerate
(
order
):
for
new_r
in
node
.
outputs
:
for
new_r
in
node
.
outputs
:
for
reason
,
r
,
old_graph_str
,
new_graph_str
in
reasons
[
new_r
]:
for
reason
,
r
,
old_graph_str
,
new_graph_str
in
reasons
[
new_r
]:
...
@@ -746,7 +806,7 @@ def _find_bad_optimizations0(order, reasons, r_vals):
...
@@ -746,7 +806,7 @@ def _find_bad_optimizations0(order, reasons, r_vals):
r_val
=
r_vals
[
r
]
r_val
=
r_vals
[
r
]
assert
r
.
type
==
new_r
.
type
assert
r
.
type
==
new_r
.
type
if
hasattr
(
new_r
,
'values_eq_approx'
):
if
hasattr
(
new_r
,
'values_eq_approx'
):
check
=
new_r
.
values_eq_approx
(
r_val
,
new_r_val
)
check
=
new_r
.
values_eq_approx
(
r_val
,
new_r_val
)
else
:
else
:
check
=
r
.
type
.
values_eq_approx
(
r_val
,
new_r_val
)
check
=
r
.
type
.
values_eq_approx
(
r_val
,
new_r_val
)
...
@@ -759,30 +819,34 @@ def _find_bad_optimizations0(order, reasons, r_vals):
...
@@ -759,30 +819,34 @@ def _find_bad_optimizations0(order, reasons, r_vals):
old_graph
=
old_graph_str
,
old_graph
=
old_graph_str
,
new_graph
=
new_graph_str
)
new_graph
=
new_graph_str
)
def
_find_bad_optimizations1
(
order
,
reasons
,
r_vals
):
def
_find_bad_optimizations1
(
order
,
reasons
,
r_vals
):
# iterate over variables looking for values that don't match the values of the
# iterate over variables looking for values that don't match the
# variables they replaced. This is the sign of a broken optimization.
# values of the variables they replaced. This is the sign of a
# broken optimization.
#identify sets of variables that are supposed to be equivalent
#identify sets of variables that are supposed to be equivalent
equivalence_sets
=
{}
equivalence_sets
=
{}
program_position
=
{}
#
node -> order idx
program_position
=
{}
#
node -> order idx
for
i
,
node
in
enumerate
(
order
):
for
i
,
node
in
enumerate
(
order
):
program_position
[
node
]
=
i
program_position
[
node
]
=
i
for
new_r
in
node
.
outputs
:
for
new_r
in
node
.
outputs
:
equivalence_sets
.
setdefault
(
new_r
,
set
([
new_r
]))
equivalence_sets
.
setdefault
(
new_r
,
set
([
new_r
]))
for
reason
,
r
,
old_graph_str
,
new_graph_str
in
reasons
[
new_r
]:
for
reason
,
r
,
old_graph_str
,
new_graph_str
in
reasons
[
new_r
]:
equivalence_sets
[
new_r
]
.
update
(
equivalence_sets
.
setdefault
(
r
,
set
([
r
])))
equivalence_sets
[
new_r
]
.
update
(
equivalence_sets
.
setdefault
(
r
,
set
([
r
])))
for
er
in
equivalence_sets
[
r
]:
for
er
in
equivalence_sets
[
r
]:
equivalence_sets
[
er
]
=
equivalence_sets
[
new_r
]
equivalence_sets
[
er
]
=
equivalence_sets
[
new_r
]
#identify equivalence sets that are broken
#identify equivalence sets that are broken
equivalence_sets_broken
=
{}
#
id(set) -> Bool
equivalence_sets_broken
=
{}
#
id(set) -> Bool
there_is_a_problem
=
False
there_is_a_problem
=
False
for
r
,
r_equiv
in
equivalence_sets
.
iteritems
():
for
r
,
r_equiv
in
equivalence_sets
.
iteritems
():
if
id
(
r_equiv
)
not
in
equivalence_sets_broken
:
if
id
(
r_equiv
)
not
in
equivalence_sets_broken
:
equivalence_sets_broken
[
id
(
r_equiv
)]
=
False
equivalence_sets_broken
[
id
(
r_equiv
)]
=
False
#loop over the variables in the set comparing them to be equal enough
#loop over the variables in the set comparing them to be
#equal enough
re0
=
None
re0
=
None
for
re
in
r_equiv
:
for
re
in
r_equiv
:
if
re0
:
if
re0
:
...
@@ -806,12 +870,15 @@ def _find_bad_optimizations1(order, reasons, r_vals):
...
@@ -806,12 +870,15 @@ def _find_bad_optimizations1(order, reasons, r_vals):
print
first_broken_set
print
first_broken_set
raise
Exception
(
'broken'
)
raise
Exception
(
'broken'
)
def
_find_bad_optimizations2
(
order
,
reasons
,
r_vals
):
def
_find_bad_optimizations2
(
order
,
reasons
,
r_vals
):
"""Use a simple algorithm to find broken optimizations.
"""Use a simple algorithm to find broken optimizations.
This algorithm is simple to understand, but sometimes when there's a problem it identifies
This algorithm is simple to understand, but sometimes when there's
the wrong optimization as the culprit. The problem stems from the fact that results are
a problem it identifies the wrong optimization as the culprit.
not evaluated in chronological order (looking at when they were introduced to the graph).
The problem stems from the fact that results are not evaluated in
chronological order (looking at when they were introduced to the
graph).
"""
"""
checked_variables
=
set
()
checked_variables
=
set
()
...
@@ -822,7 +889,8 @@ def _find_bad_optimizations2(order, reasons, r_vals):
...
@@ -822,7 +889,8 @@ def _find_bad_optimizations2(order, reasons, r_vals):
new_r_val
=
r_vals
[
new_r
]
new_r_val
=
r_vals
[
new_r
]
r_val
=
r_vals
[
r
]
r_val
=
r_vals
[
r
]
if
(
r
.
type
!=
new_r
.
type
)
or
(
not
r
.
type
.
values_eq_approx
(
r_val
,
new_r_val
)):
if
(
r
.
type
!=
new_r
.
type
)
or
(
not
r
.
type
.
values_eq_approx
(
r_val
,
new_r_val
)):
raise
BadOptimization
(
old_r
=
r
,
raise
BadOptimization
(
old_r
=
r
,
new_r
=
new_r
,
new_r
=
new_r
,
old_r_val
=
r_val
,
old_r_val
=
r_val
,
...
@@ -850,15 +918,16 @@ def _find_bad_optimizations2(order, reasons, r_vals):
...
@@ -850,15 +918,16 @@ def _find_bad_optimizations2(order, reasons, r_vals):
check_variable_norec
(
r
)
check_variable_norec
(
r
)
# iterate over variables looking for values that don't match the
#
iterate over variables looking for values that don't match the values of the
#
values of the variables they replaced. This is the sign of a
#
variables they replaced. This is the sign of a
broken optimization.
# broken optimization.
for
i
,
node
in
enumerate
(
order
):
for
i
,
node
in
enumerate
(
order
):
for
new_r
in
node
.
outputs
:
for
new_r
in
node
.
outputs
:
check_variable
(
new_r
)
check_variable
(
new_r
)
_find_bad_optimizations
=
_find_bad_optimizations0
_find_bad_optimizations
=
_find_bad_optimizations0
def
_check_preallocated_output
(
node
,
thunk
,
prealloc_modes
,
def_val
,
def
_check_preallocated_output
(
node
,
thunk
,
prealloc_modes
,
def_val
,
storage_map
,
r_vals
,
dr_vals
,
perform
,
active_order_set
):
storage_map
,
r_vals
,
dr_vals
,
perform
,
active_order_set
):
'''Try to apply thunk() on different output storages'''
'''Try to apply thunk() on different output storages'''
...
@@ -924,7 +993,8 @@ def _check_preallocated_output(node, thunk, prealloc_modes, def_val,
...
@@ -924,7 +993,8 @@ def _check_preallocated_output(node, thunk, prealloc_modes, def_val,
prealloc_maps
.
append
((
'f_contiguous'
,
f_cont_outputs
))
prealloc_maps
.
append
((
'f_contiguous'
,
f_cont_outputs
))
if
'neg_strides'
in
prealloc_maps
:
if
'neg_strides'
in
prealloc_maps
:
raise
NotImplementedError
(
'Negative strides in check_preallocated_output'
)
raise
NotImplementedError
(
'Negative strides in'
' check_preallocated_output'
)
for
(
name
,
out_map
)
in
prealloc_maps
:
for
(
name
,
out_map
)
in
prealloc_maps
:
# _logger.debug('name = %s, perform = %s', name, perform)
# _logger.debug('name = %s, perform = %s', name, perform)
...
@@ -944,7 +1014,8 @@ def _check_preallocated_output(node, thunk, prealloc_modes, def_val,
...
@@ -944,7 +1014,8 @@ def _check_preallocated_output(node, thunk, prealloc_modes, def_val,
if
not
r
.
type
.
is_valid_value
(
storage_map
[
r
][
0
]):
if
not
r
.
type
.
is_valid_value
(
storage_map
[
r
][
0
]):
raise
InvalidValueError
(
r
,
storage_map
[
r
][
0
],
raise
InvalidValueError
(
r
,
storage_map
[
r
][
0
],
hint
=
'
%
s with
%
s output'
%
(
perform
,
name
),
hint
=
'
%
s with
%
s output'
%
(
perform
,
name
),
specific_hint
=
r
.
type
.
value_validity_msg
(
storage_map
[
r
][
0
]))
specific_hint
=
r
.
type
.
value_validity_msg
(
storage_map
[
r
][
0
]))
_check_inputs
(
node
,
storage_map
,
r_vals
,
dr_vals
,
active_order_set
,
_check_inputs
(
node
,
storage_map
,
r_vals
,
dr_vals
,
active_order_set
,
clobber_dr_vals
=
False
,
clobber_dr_vals
=
False
,
...
@@ -956,16 +1027,19 @@ def _check_preallocated_output(node, thunk, prealloc_modes, def_val,
...
@@ -956,16 +1027,19 @@ def _check_preallocated_output(node, thunk, prealloc_modes, def_val,
for
r
in
node
.
outputs
:
for
r
in
node
.
outputs
:
if
not
r
.
type
.
values_eq_approx
(
r_vals
[
r
],
storage_map
[
r
][
0
]):
if
not
r
.
type
.
values_eq_approx
(
r_vals
[
r
],
storage_map
[
r
][
0
]):
# TODO: indicate it is not a C/Py problem
# TODO: indicate it is not a C/Py problem
raise
BadCLinkerOutput
(
r
,
val_py
=
r_vals
[
r
],
val_c
=
storage_map
[
r
][
0
])
raise
BadCLinkerOutput
(
r
,
val_py
=
r_vals
[
r
],
val_c
=
storage_map
[
r
][
0
])
# Clear storage_map
# Clear storage_map
for
r
in
node
.
outputs
:
for
r
in
node
.
outputs
:
storage_map
[
r
][
0
]
=
None
storage_map
[
r
][
0
]
=
None
class
_EnvEvent
(
object
):
class
_EnvEvent
(
object
):
"""A record of an event in the life of an Env.
"""A record of an event in the life of an Env.
The __eq__ function is important here, as it is the basis for comparing optimization runs.
The __eq__ function is important here, as it is the basis for
comparing optimization runs.
"""
"""
kind
=
""
kind
=
""
...
@@ -1014,8 +1088,9 @@ class _EnvEvent(object):
...
@@ -1014,8 +1088,9 @@ class _EnvEvent(object):
def
__eq__
(
self
,
other
):
def
__eq__
(
self
,
other
):
rval
=
type
(
self
)
==
type
(
other
)
rval
=
type
(
self
)
==
type
(
other
)
if
rval
:
if
rval
:
# nodes are not compared because this comparison is supposed to be true for
# nodes are not compared because this comparison is
# corresponding events that happen in different Env instances (different graphs)
# supposed to be true for corresponding events that happen
# in different Env instances (different graphs)
for
attr
in
[
'kind'
,
'op'
,
'idx'
,
'reason'
]:
for
attr
in
[
'kind'
,
'op'
,
'idx'
,
'reason'
]:
rval
=
rval
and
getattr
(
self
,
attr
)
==
getattr
(
other
,
attr
)
rval
=
rval
and
getattr
(
self
,
attr
)
==
getattr
(
other
,
attr
)
return
rval
return
rval
...
@@ -1023,6 +1098,7 @@ class _EnvEvent(object):
...
@@ -1023,6 +1098,7 @@ class _EnvEvent(object):
def
__ne__
(
self
,
other
):
def
__ne__
(
self
,
other
):
return
not
(
self
==
other
)
return
not
(
self
==
other
)
class
_VariableEquivalenceTracker
(
object
):
class
_VariableEquivalenceTracker
(
object
):
"""A Env Feature that keeps tabs on an Env and tries to detect problems."""
"""A Env Feature that keeps tabs on an Env and tries to detect problems."""
...
@@ -1100,7 +1176,8 @@ class _VariableEquivalenceTracker(object):
...
@@ -1100,7 +1176,8 @@ class _VariableEquivalenceTracker(object):
def
on_change_input
(
self
,
env
,
node
,
i
,
r
,
new_r
,
reason
=
None
):
def
on_change_input
(
self
,
env
,
node
,
i
,
r
,
new_r
,
reason
=
None
):
#print 'CHANGE by', reason, 'to use', new_r, type(new_r)
#print 'CHANGE by', reason, 'to use', new_r, type(new_r)
self
.
event_list
.
append
(
_EnvEvent
(
'change'
,
node
,
reason
=
str
(
reason
),
idx
=
i
))
self
.
event_list
.
append
(
_EnvEvent
(
'change'
,
node
,
reason
=
str
(
reason
),
idx
=
i
))
self
.
reasons
.
setdefault
(
new_r
,
[])
self
.
reasons
.
setdefault
(
new_r
,
[])
self
.
replaced_by
.
setdefault
(
new_r
,
[])
self
.
replaced_by
.
setdefault
(
new_r
,
[])
...
@@ -1111,12 +1188,12 @@ class _VariableEquivalenceTracker(object):
...
@@ -1111,12 +1188,12 @@ class _VariableEquivalenceTracker(object):
append_reason
=
False
append_reason
=
False
if
append_reason
:
if
append_reason
:
# N.B. compute the debugprint now, because future
optimizations will change the
# N.B. compute the debugprint now, because future
# graph
#
optimizations will change the
graph
self
.
reasons
[
new_r
]
.
append
((
reason
self
.
reasons
[
new_r
]
.
append
((
reason
,
,
r
r
,
,
debugprint
(
r
,
prefix
=
' '
,
depth
=
6
,
file
=
StringIO
())
.
getvalue
()
debugprint
(
r
,
prefix
=
' '
,
depth
=
6
,
file
=
StringIO
())
.
getvalue
(),
,
debugprint
(
new_r
,
prefix
=
' '
,
depth
=
6
,
file
=
StringIO
())
.
getvalue
()))
debugprint
(
new_r
,
prefix
=
' '
,
depth
=
6
,
file
=
StringIO
())
.
getvalue
()))
self
.
replaced_by
[
r
]
.
append
((
reason
,
new_r
))
self
.
replaced_by
[
r
]
.
append
((
reason
,
new_r
))
if
r
in
self
.
equiv
:
if
r
in
self
.
equiv
:
...
@@ -1134,7 +1211,6 @@ class _VariableEquivalenceTracker(object):
...
@@ -1134,7 +1211,6 @@ class _VariableEquivalenceTracker(object):
assert
new_r
in
new_r_set
assert
new_r
in
new_r_set
assert
r
in
r_set
assert
r
in
r_set
# update one equivalence set to contain the other
# update one equivalence set to contain the other
# transfer all the elements of the old one to the new one
# transfer all the elements of the old one to the new one
r_set
.
update
(
new_r_set
)
r_set
.
update
(
new_r_set
)
...
@@ -1151,6 +1227,13 @@ class _VariableEquivalenceTracker(object):
...
@@ -1151,6 +1227,13 @@ class _VariableEquivalenceTracker(object):
for
e
in
self
.
equiv
[
key
]:
for
e
in
self
.
equiv
[
key
]:
print
' '
,
e
print
' '
,
e
#List of default version of make thunk.
#This is needed to know if the user overrided it.
#The GpuOp will be added here when theano.sandbox.cuda is imported.
default_make_thunk
=
[
theano
.
gof
.
Op
.
make_thunk
.
im_func
]
class
_Linker
(
gof
.
link
.
LocalLinker
):
class
_Linker
(
gof
.
link
.
LocalLinker
):
"""Special debugging linker"""
"""Special debugging linker"""
def
__init__
(
self
,
maker
):
def
__init__
(
self
,
maker
):
...
@@ -1158,7 +1241,7 @@ class _Linker(gof.link.LocalLinker):
...
@@ -1158,7 +1241,7 @@ class _Linker(gof.link.LocalLinker):
self
.
env
=
None
self
.
env
=
None
self
.
maker
=
maker
self
.
maker
=
maker
def
accept
(
self
,
env
,
no_recycling
=
[]):
def
accept
(
self
,
env
,
no_recycling
=
[]):
if
self
.
env
is
not
None
and
self
.
env
is
not
env
:
if
self
.
env
is
not
None
and
self
.
env
is
not
env
:
assert
type
(
self
)
is
_Linker
assert
type
(
self
)
is
_Linker
return
type
(
self
)(
self
.
env
,
self
.
maker
)
.
accept
(
env
,
no_recycling
)
return
type
(
self
)(
self
.
env
,
self
.
maker
)
.
accept
(
env
,
no_recycling
)
...
@@ -1166,13 +1249,15 @@ class _Linker(gof.link.LocalLinker):
...
@@ -1166,13 +1249,15 @@ class _Linker(gof.link.LocalLinker):
self
.
no_recycling
=
no_recycling
self
.
no_recycling
=
no_recycling
return
self
return
self
def
make_all
(
self
,
profiler
=
None
,
input_storage
=
None
,
output_storage
=
None
):
def
make_all
(
self
,
profiler
=
None
,
input_storage
=
None
,
output_storage
=
None
):
if
1
:
if
1
:
#can't import at toplevel because of circular import
#can't import at toplevel because of circular import TODO:
# TODO: don't do this ugly hacky way of setting the filter_checks_isfinite
# don't do this ugly hacky way of setting the
from
theano.tensor
import
TensorType
#to set filter_check_isfinite
# filter_checks_isfinite
from
theano
import
tests
# for config.unittests.rseed
from
theano.tensor
import
TensorType
# to set filter_check_isfinite
from
theano
import
tests
# for config.unittests.rseed
env
=
self
.
env
env
=
self
.
env
input_storage_
=
input_storage
input_storage_
=
input_storage
output_storage_
=
output_storage
output_storage_
=
output_storage
...
@@ -1184,16 +1269,16 @@ class _Linker(gof.link.LocalLinker):
...
@@ -1184,16 +1269,16 @@ class _Linker(gof.link.LocalLinker):
order_outputs
.
reverse
()
order_outputs
.
reverse
()
order
=
graph
.
io_toposort
(
env
.
inputs
,
order_outputs
)
order
=
graph
.
io_toposort
(
env
.
inputs
,
order_outputs
)
active_order
=
env
.
toposort
()
#an ordering of just the active nodes
active_order
=
env
.
toposort
()
#
an ordering of just the active nodes
active_order_set
=
set
(
active_order
)
active_order_set
=
set
(
active_order
)
no_recycling
=
self
.
no_recycling
no_recycling
=
self
.
no_recycling
input_storage
,
output_storage
,
storage_map
=
link
.
map_storage
(
env
,
order
,
input_storage
,
output_storage
,
storage_map
=
link
.
map_storage
(
input_storage_
,
output_storage_
)
env
,
order
,
input_storage_
,
output_storage_
)
thunks_py
=
[]
#
python thunks
thunks_py
=
[]
#
python thunks
thunks_c
=
[]
#
c thunks
thunks_c
=
[]
#
c thunks
for
node
in
order
:
for
node
in
order
:
node_input_storage
=
[
storage_map
[
r
]
for
r
in
node
.
inputs
]
node_input_storage
=
[
storage_map
[
r
]
for
r
in
node
.
inputs
]
...
@@ -1209,14 +1294,16 @@ class _Linker(gof.link.LocalLinker):
...
@@ -1209,14 +1294,16 @@ class _Linker(gof.link.LocalLinker):
if
not
isinstance
(
node
.
op
,
gof
.
op
.
Op
):
if
not
isinstance
(
node
.
op
,
gof
.
op
.
Op
):
raise
utils
.
MethodNotDefined
()
raise
utils
.
MethodNotDefined
()
e
=
Env
(
*
graph
.
clone
(
node
.
inputs
,
node
.
outputs
))
e
=
Env
(
*
graph
.
clone
(
node
.
inputs
,
node
.
outputs
))
e
.
toposort
=
lambda
:
e
.
nodes
#
WARNING: STOCHASTIC ORDER
e
.
toposort
=
lambda
:
e
.
nodes
#
WARNING: STOCHASTIC ORDER
# Specifically... e.nodes is a set, but of only 1 element
# Specifically... e.nodes is a set, but of only 1 element
cl
=
CLinker
()
.
accept
(
e
,
[
r
for
r
,
r2
in
zip
(
e
.
outputs
,
node
.
outputs
)
if
r2
in
no_recycling
])
cl
=
CLinker
()
.
accept
(
e
,
[
r
for
r
,
r2
in
zip
(
e
.
outputs
,
node
.
outputs
)
if
r2
in
no_recycling
])
thunk
,
node_input_filters
,
node_output_filters
=
cl
.
make_thunk
(
thunk
,
node_input_filters
,
node_output_filters
=
cl
.
make_thunk
(
input_storage
=
node_input_storage
,
input_storage
=
node_input_storage
,
output_storage
=
node_output_storage
)
output_storage
=
node_output_storage
)
thunk
.
inputs
=
node_input_storage
thunk
.
inputs
=
node_input_storage
thunk
.
outputs
=
node_output_storage
thunk
.
outputs
=
node_output_storage
thunks_c
.
append
(
thunk
)
thunks_c
.
append
(
thunk
)
...
@@ -1229,8 +1316,9 @@ class _Linker(gof.link.LocalLinker):
...
@@ -1229,8 +1316,9 @@ class _Linker(gof.link.LocalLinker):
if
((
self
.
maker
.
mode
.
check_py_code
or
thunks_c
[
-
1
]
is
None
)
and
if
((
self
.
maker
.
mode
.
check_py_code
or
thunks_c
[
-
1
]
is
None
)
and
node
.
op
.
perform
.
func_code
!=
gof
.
op
.
PureOp
.
perform
.
func_code
):
node
.
op
.
perform
.
func_code
!=
gof
.
op
.
PureOp
.
perform
.
func_code
):
p
=
node
.
op
.
perform
p
=
node
.
op
.
perform
thunk
=
(
lambda
p
=
p
,
i
=
node_input_storage
,
o
=
node_output_storage
,
n
=
thunk
=
(
lambda
p
=
p
,
i
=
node_input_storage
,
node
:
p
(
n
,
[
x
[
0
]
for
x
in
i
],
o
))
o
=
node_output_storage
,
n
=
node
:
p
(
n
,
[
x
[
0
]
for
x
in
i
],
o
))
thunk
.
inputs
=
node_input_storage
thunk
.
inputs
=
node_input_storage
thunk
.
outputs
=
node_output_storage
thunk
.
outputs
=
node_output_storage
thunk
.
perform
=
p
thunk
.
perform
=
p
...
@@ -1239,7 +1327,7 @@ class _Linker(gof.link.LocalLinker):
...
@@ -1239,7 +1327,7 @@ class _Linker(gof.link.LocalLinker):
thunks_py
.
append
(
None
)
thunks_py
.
append
(
None
)
# If the op define its own make_thunk, check it
# If the op define its own make_thunk, check it
if
node
.
op
.
make_thunk
.
im_func
!=
theano
.
gof
.
Op
.
make_thunk
.
im_func
:
if
node
.
op
.
make_thunk
.
im_func
not
in
default_make_thunk
:
compute_map
=
{}
compute_map
=
{}
for
k
in
node
.
inputs
:
for
k
in
node
.
inputs
:
compute_map
[
k
]
=
[
True
]
compute_map
[
k
]
=
[
True
]
...
@@ -1276,14 +1364,15 @@ class _Linker(gof.link.LocalLinker):
...
@@ -1276,14 +1364,15 @@ class _Linker(gof.link.LocalLinker):
elif
thunks_c
[
-
1
]
is
None
:
elif
thunks_c
[
-
1
]
is
None
:
thunks_c
[
-
1
]
=
thunk
thunks_c
[
-
1
]
=
thunk
else
:
else
:
_logger
.
warn
(
"We won't check the perform function of node '
%
s' but we will check its make_thunk function"
%
node
)
_logger
.
warn
(
"We won't check the perform function of node '
%
s' but we will check its make_thunk function"
%
node
)
thunks_py
[
-
1
]
=
thunk
thunks_py
[
-
1
]
=
thunk
if
no_recycling
is
True
:
if
no_recycling
is
True
:
no_recycling
=
storage_map
.
values
()
no_recycling
=
storage_map
.
values
()
no_recycling
=
utils
.
difference
(
no_recycling
,
input_storage
)
no_recycling
=
utils
.
difference
(
no_recycling
,
input_storage
)
else
:
else
:
no_recycling
=
[
storage_map
[
r
]
for
r
in
no_recycling
if
r
not
in
env
.
inputs
]
no_recycling
=
[
storage_map
[
r
]
for
r
in
no_recycling
if
r
not
in
env
.
inputs
]
# Precompute some things for storage pre-allocation
# Precompute some things for storage pre-allocation
prealloc_modes
=
config
.
DebugMode
.
check_preallocated_output
.
split
(
':'
)
prealloc_modes
=
config
.
DebugMode
.
check_preallocated_output
.
split
(
':'
)
...
@@ -1305,16 +1394,18 @@ class _Linker(gof.link.LocalLinker):
...
@@ -1305,16 +1394,18 @@ class _Linker(gof.link.LocalLinker):
for
x
in
no_recycling
:
for
x
in
no_recycling
:
x
[
0
]
=
None
x
[
0
]
=
None
# nest all this in try-finally to put storage *back* into storage_map when an
# nest all this in try-finally to put storage *back* into
# exception is raised
# storage_map when an exception is raised
original_storage_map_keys
=
[
r
for
r
in
storage_map
if
r
.
owner
is
None
]
original_storage_map_keys
=
[
r
for
r
in
storage_map
if
r
.
owner
is
None
]
try
:
try
:
equiv_vals
=
{}
equiv_vals
=
{}
problematic
=
set
()
problematic
=
set
()
# r_vals are the true values associated with each variable in the graph
# r_vals are the true values associated with each
# they should not change during the evaluation of this function, even when the
# variable in the graph they should not change during
# graph has destructive ops in it
# the evaluation of this function, even when the graph
# has destructive ops in it
#
#
# This dictionary is used to populate the storage_map as necessary
# This dictionary is used to populate the storage_map as necessary
r_vals
=
{}
r_vals
=
{}
...
@@ -1336,7 +1427,7 @@ class _Linker(gof.link.LocalLinker):
...
@@ -1336,7 +1427,7 @@ class _Linker(gof.link.LocalLinker):
# an error if it is not valid.
# an error if it is not valid.
if
(
storage_map
[
r
][
0
]
is
None
):
if
(
storage_map
[
r
][
0
]
is
None
):
raise
InvalidValueError
(
r
,
storage_map
[
r
][
0
],
raise
InvalidValueError
(
r
,
storage_map
[
r
][
0
],
hint
=
"Graph Input '
%
s' is missing"
%
str
(
r
))
hint
=
"Graph Input '
%
s' is missing"
%
str
(
r
))
raise
InvalidValueError
(
r
,
storage_map
[
r
][
0
],
raise
InvalidValueError
(
r
,
storage_map
[
r
][
0
],
hint
=
(
"Graph Input '
%
s' has invalid value "
hint
=
(
"Graph Input '
%
s' has invalid value "
"
%
s"
%
(
r
,
storage_map
[
r
][
0
])))
"
%
s"
%
(
r
,
storage_map
[
r
][
0
])))
...
@@ -1351,7 +1442,8 @@ class _Linker(gof.link.LocalLinker):
...
@@ -1351,7 +1442,8 @@ class _Linker(gof.link.LocalLinker):
storage_map
[
r
][
0
]
=
None
storage_map
[
r
][
0
]
=
None
#####
#####
# Precondition: the storage map is empty, transferred completely to r_vals
# Precondition: the storage map is empty, transferred
# completely to r_vals
#####
#####
for
r
,
s
in
storage_map
.
iteritems
():
for
r
,
s
in
storage_map
.
iteritems
():
if
s
[
0
]
is
not
None
:
if
s
[
0
]
is
not
None
:
...
@@ -1360,7 +1452,9 @@ class _Linker(gof.link.LocalLinker):
...
@@ -1360,7 +1452,9 @@ class _Linker(gof.link.LocalLinker):
#try:
#try:
# compute the value of all variables
# compute the value of all variables
for
i
,
(
thunk_py
,
thunk_c
,
node
)
in
enumerate
(
zip
(
thunks_py
,
thunks_c
,
order
)):
for
i
,
(
thunk_py
,
thunk_c
,
node
)
in
enumerate
(
zip
(
thunks_py
,
thunks_c
,
order
)):
this_node_destroyed_variables
=
set
()
this_node_destroyed_variables
=
set
()
_logger
.
debug
(
"
%
i - starting node
%
i
%
s"
,
i
,
i
,
node
)
_logger
.
debug
(
"
%
i - starting node
%
i
%
s"
,
i
,
i
,
node
)
...
@@ -1373,24 +1467,33 @@ class _Linker(gof.link.LocalLinker):
...
@@ -1373,24 +1467,33 @@ class _Linker(gof.link.LocalLinker):
# print >> sys.stderr,i, "DEBUGMODE: deepcopy input ", r
# print >> sys.stderr,i, "DEBUGMODE: deepcopy input ", r
storage_map
[
r
][
0
]
=
_lessbroken_deepcopy
(
r_vals
[
r
])
storage_map
[
r
][
0
]
=
_lessbroken_deepcopy
(
r_vals
[
r
])
if
not
r
.
type
.
is_valid_value
(
storage_map
[
r
][
0
]):
if
not
r
.
type
.
is_valid_value
(
storage_map
[
r
][
0
]):
raise
InvalidValueError
(
r
,
storage_map
[
r
][
0
],
client_node
=
node
)
raise
InvalidValueError
(
r
,
storage_map
[
r
][
0
],
client_node
=
node
)
## On the first call to thunk_py(), its output storage will be None
## On the first call to thunk_py(), its output
## storage will be None
if
thunk_py
:
if
thunk_py
:
_logger
.
debug
(
"
%
i - running thunk_py with None as "
_logger
.
debug
(
"
%
i - running thunk_py with None as "
"output storage"
,
i
)
"output storage"
,
i
)
try
:
try
:
thunk_py
()
thunk_py
()
except
utils
.
MethodNotDefined
:
except
utils
.
MethodNotDefined
:
thunk_py
=
None
#shouldn't have put it into the list in the first place
# shouldn't have put it into the list in
# the first place
thunk_py
=
None
if
thunk_py
:
if
thunk_py
:
# check output values for type-correctness
# check output values for type-correctness
for
r
in
node
.
outputs
:
for
r
in
node
.
outputs
:
if
not
r
.
type
.
is_valid_value
(
storage_map
[
r
][
0
]):
if
not
r
.
type
.
is_valid_value
(
storage_map
[
r
][
0
]):
raise
InvalidValueError
(
r
,
storage_map
[
r
][
0
],
hint
=
'perform output'
,
specific_hint
=
r
.
type
.
value_validity_msg
(
storage_map
[
r
][
0
]))
hint2
=
r
.
type
.
value_validity_msg
(
storage_map
[
r
][
0
])
raise
InvalidValueError
(
r
,
storage_map
[
r
][
0
],
hint
=
'perform output'
,
specific_hint
=
hint2
)
_check_inputs
(
node
,
storage_map
,
r_vals
,
dr_vals
,
active_order_set
,
_check_inputs
(
node
,
storage_map
,
r_vals
,
dr_vals
,
active_order_set
,
clobber_dr_vals
=
True
,
perform
=
'py'
,
clobber_dr_vals
=
True
,
perform
=
'py'
,
warn_input_not_reused
=
config
.
DebugMode
.
warn_input_not_reused
)
warn_input_not_reused
=
config
.
DebugMode
.
warn_input_not_reused
)
...
@@ -1402,7 +1505,8 @@ class _Linker(gof.link.LocalLinker):
...
@@ -1402,7 +1505,8 @@ class _Linker(gof.link.LocalLinker):
assert
r
not
in
r_vals
assert
r
not
in
r_vals
# print >> sys.stderr, i, "DEBUGMODE storing reference output %x" % id(storage_map[r][0])
# print >> sys.stderr, i, "DEBUGMODE storing reference output %x" % id(storage_map[r][0])
r_vals
[
r
]
=
storage_map
[
r
][
0
]
r_vals
[
r
]
=
storage_map
[
r
][
0
]
storage_map
[
r
][
0
]
=
None
#clear the storage_map of outputs for the thunk_c
# clear the storage_map of outputs for the thunk_c
storage_map
[
r
][
0
]
=
None
if
config
.
DebugMode
.
check_preallocated_output
:
if
config
.
DebugMode
.
check_preallocated_output
:
_logger
.
debug
(
_logger
.
debug
(
...
@@ -1463,10 +1567,13 @@ class _Linker(gof.link.LocalLinker):
...
@@ -1463,10 +1567,13 @@ class _Linker(gof.link.LocalLinker):
if
thunk_py
:
if
thunk_py
:
assert
r
in
r_vals
#because we put it in during the thunk_py branch
assert
r
in
r_vals
#because we put it in during the thunk_py branch
# check for stride correctness (may raise exception)
# check for stride correctness (may raise exception)
_check_strides_match
(
r_vals
[
r
],
storage_map
[
r
][
0
],
_check_strides_match
(
r_vals
[
r
],
self
.
maker
.
mode
.
require_matching_strides
,
node
.
op
)
storage_map
[
r
][
0
],
self
.
maker
.
mode
.
require_matching_strides
,
node
.
op
)
_check_inputs
(
node
,
storage_map
,
r_vals
,
dr_vals
,
active_order_set
,
_check_inputs
(
node
,
storage_map
,
r_vals
,
dr_vals
,
active_order_set
,
clobber_dr_vals
=
clobber
,
perform
=
'c'
,
clobber_dr_vals
=
clobber
,
perform
=
'c'
,
warn_input_not_reused
=
config
.
DebugMode
.
warn_input_not_reused
)
warn_input_not_reused
=
config
.
DebugMode
.
warn_input_not_reused
)
...
@@ -1513,7 +1620,6 @@ class _Linker(gof.link.LocalLinker):
...
@@ -1513,7 +1620,6 @@ class _Linker(gof.link.LocalLinker):
#[(id(o), numpy.asarray(storage_map[o][0])[0,0]) for o in node.outputs])
#[(id(o), numpy.asarray(storage_map[o][0])[0,0]) for o in node.outputs])
sys
.
stdout
.
flush
()
sys
.
stdout
.
flush
()
# we're done with this thunk
# we're done with this thunk
# clear everything out of the storage_map
# clear everything out of the storage_map
for
r
in
node
.
inputs
:
for
r
in
node
.
inputs
:
...
@@ -1526,14 +1632,16 @@ class _Linker(gof.link.LocalLinker):
...
@@ -1526,14 +1632,16 @@ class _Linker(gof.link.LocalLinker):
#But it is very slow and it is not sure it will help.
#But it is very slow and it is not sure it will help.
gc
.
collect
()
gc
.
collect
()
_find_bad_optimizations
(
order
,
env
.
equivalence_tracker
.
reasons
,
r_vals
)
_find_bad_optimizations
(
order
,
env
.
equivalence_tracker
.
reasons
,
r_vals
)
#####
#####
# Postcondition: the input and output variables are in the storage map, nothing more
# Postcondition: the input and output variables are
# in the storage map, nothing more
#####
#####
# Nothing should be in storage map after evaluating
each the thunk (specifically the
# Nothing should be in storage map after evaluating
# last one)
#
each the thunk (specifically the
last one)
for
r
,
s
in
storage_map
.
iteritems
():
for
r
,
s
in
storage_map
.
iteritems
():
assert
type
(
s
)
is
list
assert
type
(
s
)
is
list
assert
s
[
0
]
is
None
assert
s
[
0
]
is
None
...
@@ -1585,7 +1693,6 @@ class _Linker(gof.link.LocalLinker):
...
@@ -1585,7 +1693,6 @@ class _Linker(gof.link.LocalLinker):
if
not
r
.
type
.
is_valid_value
(
None
):
if
not
r
.
type
.
is_valid_value
(
None
):
assert
storage_map
[
r
][
0
]
is
not
None
assert
storage_map
[
r
][
0
]
is
not
None
###############
###############
# Done debugmode function call 'f'
# Done debugmode function call 'f'
##############
##############
...
@@ -1610,17 +1717,20 @@ class _Linker(gof.link.LocalLinker):
...
@@ -1610,17 +1717,20 @@ class _Linker(gof.link.LocalLinker):
assert
len
(
env
.
inputs
)
==
len
(
input_storage
)
assert
len
(
env
.
inputs
)
==
len
(
input_storage
)
assert
len
(
env
.
outputs
)
==
len
(
output_storage
)
assert
len
(
env
.
outputs
)
==
len
(
output_storage
)
#print 'make_all returning output', [id(z) for z in output_storage]
#print 'make_all returning output', [id(z) for z in output_storage]
return
f
,
[
link
.
Container
(
input
,
storage
,
readonly
=
False
)
for
input
,
storage
in
zip
(
env
.
inputs
,
input_storage
)],
\
return
f
,
[
link
.
Container
(
input
,
storage
,
readonly
=
False
)
[
link
.
Container
(
output
,
storage
,
readonly
=
True
)
for
output
,
storage
in
zip
(
env
.
outputs
,
output_storage
)],
\
for
input
,
storage
in
zip
(
env
.
inputs
,
input_storage
)],
\
thunks_py
,
order
[
link
.
Container
(
output
,
storage
,
readonly
=
True
)
for
output
,
storage
in
zip
(
env
.
outputs
,
output_storage
)],
\
thunks_py
,
order
_NODEFAULT
=
[
'NODEFAULT'
]
_NODEFAULT
=
[
'NODEFAULT'
]
class
_Maker
(
FunctionMaker
):
#
inheritance buys a few helper functions
class
_Maker
(
FunctionMaker
):
#
inheritance buys a few helper functions
"""Special debugging FunctionMaker
"""Special debugging FunctionMaker
"""
"""
verbose
=
0
verbose
=
0
"""Verbosity level of compile-time and run-time checks. (Default
0: silent)"""
"""Verbosity level of compile-time and run-time checks. (Default
0: silent)"""
def
__init__
(
self
,
inputs
,
outputs
,
optimizer
,
mode
,
def
__init__
(
self
,
inputs
,
outputs
,
optimizer
,
mode
,
accept_inplace
=
False
,
accept_inplace
=
False
,
...
@@ -1634,14 +1744,17 @@ class _Maker(FunctionMaker): #inheritance buys a few helper functions
...
@@ -1634,14 +1744,17 @@ class _Maker(FunctionMaker): #inheritance buys a few helper functions
case the functions produced by FunctionMaker will return
case the functions produced by FunctionMaker will return
their output value directly
their output value directly
:param accept_inplace: True iff it is acceptable to have inplace operations
:param accept_inplace: True iff it is acceptable to have
in the graph from the inputs to the outputs
inplace operations in the graph from the inputs to
the outputs
:note: this function sets TensorType.filter_checks_isfinite when `mode.check_isfinite` is True
:note: this function sets TensorType.filter_checks_isfinite
when `mode.check_isfinite` is True
"""
"""
self
.
profile
=
profile
self
.
profile
=
profile
# Handle the case where inputs and/or outputs is a single Variable (not in a list)
# Handle the case where inputs and/or outputs is a single
# Variable (not in a list)
unpack_single
=
False
unpack_single
=
False
return_none
=
False
return_none
=
False
if
outputs
is
None
:
if
outputs
is
None
:
...
@@ -1655,17 +1768,21 @@ class _Maker(FunctionMaker): #inheritance buys a few helper functions
...
@@ -1655,17 +1768,21 @@ class _Maker(FunctionMaker): #inheritance buys a few helper functions
# Wrap them in In or Out instances if needed.
# Wrap them in In or Out instances if needed.
inputs
,
outputs
=
map
(
self
.
wrap_in
,
inputs
),
map
(
self
.
wrap_out
,
outputs
)
inputs
,
outputs
=
map
(
self
.
wrap_in
,
inputs
),
map
(
self
.
wrap_out
,
outputs
)
_inputs
=
gof
.
graph
.
inputs
([
o
.
variable
for
o
in
outputs
]
+
[
i
.
update
for
i
in
inputs
if
getattr
(
i
,
'update'
,
False
)])
_inputs
=
gof
.
graph
.
inputs
([
o
.
variable
for
o
in
outputs
]
+
[
i
.
update
for
i
in
inputs
if
getattr
(
i
,
'update'
,
False
)])
#TODO: REMOVE THIS CRUFT - it's complicated for SymbolicInputKits
#TODO: REMOVE THIS CRUFT - it's complicated for SymbolicInputKits
indices
=
[[
input
]
+
self
.
expand_in
(
input
,
_inputs
)
for
input
in
inputs
]
indices
=
[[
input
]
+
self
.
expand_in
(
input
,
_inputs
)
for
input
in
inputs
]
expanded_inputs
=
reduce
(
list
.
__add__
,
[
list
(
z
)
for
x
,
y
,
z
in
indices
],
[])
expanded_inputs
=
reduce
(
list
.
__add__
,
[
list
(
z
)
for
x
,
y
,
z
in
indices
],
[])
assert
expanded_inputs
==
inputs
#JB - I added this to make sure we could delete above
assert
expanded_inputs
==
inputs
#JB - I added this to make sure we could delete above
# make the env
# make the env
for
i
in
xrange
(
mode
.
stability_patience
):
for
i
in
xrange
(
mode
.
stability_patience
):
env
,
additional_outputs
,
equivalence_tracker
=
_optcheck_env
(
expanded_inputs
,
outputs
,
accept_inplace
)
env
,
additional_outputs
,
equivalence_tracker
=
_optcheck_env
(
expanded_inputs
,
outputs
,
accept_inplace
)
env
.
equivalence_tracker
=
equivalence_tracker
env
.
equivalence_tracker
=
equivalence_tracker
# optimize the env
# optimize the env
...
@@ -1674,7 +1791,8 @@ class _Maker(FunctionMaker): #inheritance buys a few helper functions
...
@@ -1674,7 +1791,8 @@ class _Maker(FunctionMaker): #inheritance buys a few helper functions
theano
.
config
.
compute_test_value
=
"off"
theano
.
config
.
compute_test_value
=
"off"
optimizer
(
env
)
optimizer
(
env
)
theano
.
compile
.
function_module
.
insert_deepcopy
(
env
,
inputs
,
outputs
+
additional_outputs
)
theano
.
compile
.
function_module
.
insert_deepcopy
(
env
,
inputs
,
outputs
+
additional_outputs
)
finally
:
finally
:
theano
.
config
.
compute_test_value
=
compute_test_value_orig
theano
.
config
.
compute_test_value
=
compute_test_value_orig
...
@@ -1743,7 +1861,7 @@ class _Maker(FunctionMaker): #inheritance buys a few helper functions
...
@@ -1743,7 +1861,7 @@ class _Maker(FunctionMaker): #inheritance buys a few helper functions
self
.
function_builder
=
function_builder
self
.
function_builder
=
function_builder
self
.
mode
=
mode
self
.
mode
=
mode
def
create
(
self
,
defaults
=
None
,
trustme
=
False
):
def
create
(
self
,
defaults
=
None
,
trustme
=
False
):
"""
"""
Create a function.
Create a function.
...
@@ -1754,7 +1872,7 @@ class _Maker(FunctionMaker): #inheritance buys a few helper functions
...
@@ -1754,7 +1872,7 @@ class _Maker(FunctionMaker): #inheritance buys a few helper functions
trustme -> disables some exceptions, used internally
trustme -> disables some exceptions, used internally
"""
"""
if
defaults
is
None
:
if
defaults
is
None
:
defaults
=
[
None
]
*
len
(
self
.
inputs
)
defaults
=
[
None
]
*
len
(
self
.
inputs
)
input_storage
=
[]
# list of independent one-element lists, will be passed to the linker
input_storage
=
[]
# list of independent one-element lists, will be passed to the linker
_defaults
=
[]
_defaults
=
[]
...
@@ -1807,10 +1925,11 @@ class _Maker(FunctionMaker): #inheritance buys a few helper functions
...
@@ -1807,10 +1925,11 @@ class _Maker(FunctionMaker): #inheritance buys a few helper functions
else
:
else
:
_defaults
.
append
((
False
,
False
,
default
))
_defaults
.
append
((
False
,
False
,
default
))
elif
input
.
update
is
not
None
:
elif
input
.
update
is
not
None
:
# If the input has an update, then (logically) it is not required since
# If the input has an update, then (logically) it is
# it is just a parameter and of course we don't want to refeed the default
# not required since it is just a parameter and of
# back into the storage as it would defeat the point of updating it. We
# course we don't want to refeed the default back into
# always do this policy.
# the storage as it would defeat the point of updating
# it. We always do this policy.
if
default
is
None
:
if
default
is
None
:
if
trustme
or
isinstance
(
__default
,
gof
.
Container
):
if
trustme
or
isinstance
(
__default
,
gof
.
Container
):
_defaults
.
append
((
False
,
False
,
None
))
_defaults
.
append
((
False
,
False
,
None
))
...
@@ -1824,19 +1943,26 @@ class _Maker(FunctionMaker): #inheritance buys a few helper functions
...
@@ -1824,19 +1943,26 @@ class _Maker(FunctionMaker): #inheritance buys a few helper functions
if
trustme
or
isinstance
(
__default
,
gof
.
Container
):
if
trustme
or
isinstance
(
__default
,
gof
.
Container
):
_defaults
.
append
((
False
,
False
,
None
))
_defaults
.
append
((
False
,
False
,
None
))
else
:
else
:
# No default, so this is a required input. Nothing to feed back, initial value is None.
# No default, so this is a required
# input. Nothing to feed back, initial value
# is None.
_defaults
.
append
((
True
,
False
,
None
))
_defaults
.
append
((
True
,
False
,
None
))
else
:
else
:
# Default value. It is not required, but we want to put it back into the storage
# Default value. It is not required, but we want
# everytime so it behaves like most programming languages' default values
# to put it back into the storage everytime so it
# behaves like most programming languages' default
# values
_defaults
.
append
((
False
,
True
,
default
))
_defaults
.
append
((
False
,
True
,
default
))
defaults
=
_defaults
defaults
=
_defaults
# Get a function instance
# Get a function instance
_fn
,
_i
,
_o
=
self
.
linker
.
make_thunk
(
input_storage
=
input_storage
)
_fn
,
_i
,
_o
=
self
.
linker
.
make_thunk
(
input_storage
=
input_storage
)
fn
=
self
.
function_builder
(
_fn
,
_i
,
_o
,
self
.
indices
,
self
.
outputs
,
defaults
,
self
.
unpack_single
,
self
.
return_none
,
self
)
fn
=
self
.
function_builder
(
_fn
,
_i
,
_o
,
self
.
indices
,
self
.
outputs
,
defaults
,
self
.
unpack_single
,
self
.
return_none
,
self
)
return
fn
return
fn
def
_pickle_DebugMode_Maker
(
maker
):
def
_pickle_DebugMode_Maker
(
maker
):
raise
NotImplementedError
(
'DebugMode is not picklable (yet)'
)
raise
NotImplementedError
(
'DebugMode is not picklable (yet)'
)
copy_reg
.
pickle
(
_Maker
,
_pickle_DebugMode_Maker
)
copy_reg
.
pickle
(
_Maker
,
_pickle_DebugMode_Maker
)
...
@@ -1847,6 +1973,7 @@ copy_reg.pickle(_Maker, _pickle_DebugMode_Maker)
...
@@ -1847,6 +1973,7 @@ copy_reg.pickle(_Maker, _pickle_DebugMode_Maker)
#
#
########################
########################
class
DebugMode
(
Mode
):
class
DebugMode
(
Mode
):
"""Evaluation Mode that detects internal theano errors.
"""Evaluation Mode that detects internal theano errors.
...
@@ -1854,22 +1981,24 @@ class DebugMode(Mode):
...
@@ -1854,22 +1981,24 @@ class DebugMode(Mode):
- inconsistent c_code and perform implementations (see `BadCLinkerOutput`)
- inconsistent c_code and perform implementations (see `BadCLinkerOutput`)
- a variable replacing another when their runtime values don't match. This is a symptom of
- a variable replacing another when their runtime values don't
an incorrect optimization step, or faulty Op implementation (raises `BadOptimization`)
match. This is a symptom of an incorrect optimization step, or
faulty Op implementation (raises `BadOptimization`)
- stochastic optimization ordering (raises `StochasticOrder`)
- stochastic optimization ordering (raises `StochasticOrder`)
- incomplete `destroy_map` specification (raises `BadDestroyMap`)
- incomplete `destroy_map` specification (raises `BadDestroyMap`)
- an op that returns an illegal value not matching the output
Variable Type (raises
- an op that returns an illegal value not matching the output
InvalidValueError)
Variable Type (raises
InvalidValueError)
Each of these exceptions inherits from the more generic `DebugModeError`.
Each of these exceptions inherits from the more generic `DebugModeError`.
If there are no internal errors, this mode behaves like FAST_RUN
or FAST_COMPILE, but takes
If there are no internal errors, this mode behaves like FAST_RUN
a little longer and uses more memory.
or FAST_COMPILE, but takes
a little longer and uses more memory.
If there are internal errors, this mode will raise an `DebugModeError` exception.
If there are internal errors, this mode will raise an
`DebugModeError` exception.
:remark: The work of debugging is implemented by the `_Maker`, `_Linker`, and
:remark: The work of debugging is implemented by the `_Maker`, `_Linker`, and
`_VariableEquivalenceTracker` classes.
`_VariableEquivalenceTracker` classes.
...
@@ -1878,7 +2007,8 @@ class DebugMode(Mode):
...
@@ -1878,7 +2007,8 @@ class DebugMode(Mode):
stability_patience
=
config
.
DebugMode
.
patience
stability_patience
=
config
.
DebugMode
.
patience
"""
"""
When checking for the stability of optimization, recompile the graph this many times.
When checking for the stability of optimization, recompile the
graph this many times.
"""
"""
check_c_code
=
config
.
DebugMode
.
check_c
check_c_code
=
config
.
DebugMode
.
check_c
...
@@ -1898,13 +2028,14 @@ class DebugMode(Mode):
...
@@ -1898,13 +2028,14 @@ class DebugMode(Mode):
require_matching_strides
=
config
.
DebugMode
.
check_strides
require_matching_strides
=
config
.
DebugMode
.
check_strides
"""
"""
Should we check for (and complain about) Ops whose python and C outputs are ndarrays with
Should we check for (and complain about) Ops whose python and C
different strides? (This can catch bugs, but is generally overly strict.) 0 no check, 1 warn, 2 err.
outputs are ndarrays with different strides? (This can catch bugs,
but is generally overly strict.) 0 no check, 1 warn, 2 err.
"""
"""
# This function will be used to create a FunctionMaker in
# This function will be used to create a FunctionMaker in
# function_module.function
# function_module.function
def
function_maker
(
self
,
i
,
o
,
m
,
*
args
,
**
kwargs
):
def
function_maker
(
self
,
i
,
o
,
m
,
*
args
,
**
kwargs
):
"""Return an instance of `_Maker` which handles much of the debugging work"""
"""Return an instance of `_Maker` which handles much of the debugging work"""
assert
m
is
self
assert
m
is
self
return
_Maker
(
i
,
o
,
self
.
optimizer
,
self
,
*
args
,
**
kwargs
)
return
_Maker
(
i
,
o
,
self
.
optimizer
,
self
,
*
args
,
**
kwargs
)
...
@@ -1947,4 +2078,4 @@ class DebugMode(Mode):
...
@@ -1947,4 +2078,4 @@ class DebugMode(Mode):
if
not
(
self
.
check_c_code
or
self
.
check_py_code
):
if
not
(
self
.
check_c_code
or
self
.
check_py_code
):
raise
ValueError
(
'DebugMode has to check at least one of c and py code'
)
raise
ValueError
(
'DebugMode has to check at least one of c and py code'
)
register_mode
(
'DEBUG_MODE'
,
DebugMode
(
optimizer
=
'fast_run'
))
register_mode
(
'DEBUG_MODE'
,
DebugMode
(
optimizer
=
'fast_run'
))
theano/sandbox/cuda/__init__.py
浏览文件 @
74324316
...
@@ -179,6 +179,7 @@ class GpuOp(theano.gof.Op):
...
@@ -179,6 +179,7 @@ class GpuOp(theano.gof.Op):
return
super
(
GpuOp
,
self
)
.
make_thunk
(
node
,
storage_map
,
return
super
(
GpuOp
,
self
)
.
make_thunk
(
node
,
storage_map
,
compute_map
,
no_recycling
)
compute_map
,
no_recycling
)
theano
.
compile
.
debugmode
.
default_make_thunk
.
append
(
GpuOp
.
make_thunk
.
im_func
)
# We must do those import to be able to create the full doc when
# We must do those import to be able to create the full doc when
# nvcc is not available
# nvcc is not available
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论