Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
P
pytensor
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
testgroup
pytensor
Commits
8e9592b7
提交
8e9592b7
authored
2月 25, 2009
作者:
James Bergstra
浏览文件
操作
浏览文件
下载
差异文件
merge
上级
4ea9596a
51bb1168
隐藏空白字符变更
内嵌
并排
正在显示
5 个修改的文件
包含
428 行增加
和
102 行删除
+428
-102
type.py
theano/gof/type.py
+16
-0
debugmode.py
theano/sandbox/debugmode.py
+192
-99
test_debugmode.py
theano/sandbox/test_debugmode.py
+214
-1
basic.py
theano/tensor/basic.py
+4
-0
test_opt.py
theano/tensor/tests/test_opt.py
+2
-2
没有找到文件。
theano/gof/type.py
浏览文件 @
8e9592b7
...
...
@@ -242,6 +242,22 @@ class PureType(object):
r
.
tag
.
trace
=
traceback
.
extract_stack
()[:
-
1
]
return
r
def
values_eq_enough
(
self
,
a
,
b
):
"""Return True if a and b can be considered equal as Op outputs, else False.
:param a: a potential value for a Result of this Type.
:param b: a potential value for a Result of this Type.
:rtype: Bool
This function is used by theano debugging tools to decide whether two values are
equivalent, admitting a certain amount of numerical instability. For example,
for floating-point numbers this function should be an approximate comparison.
"""
return
(
a
==
b
)
_nothing
=
"""
"""
...
...
theano/sandbox/debugmode.py
浏览文件 @
8e9592b7
""" Provides `
OptCheck
`
""" Provides `
DebugMode
`
"""
...
...
@@ -25,6 +25,76 @@ from ..compile.function_module import (convert_function_input,
SymbolicOutput
,
Supervisor
)
class
DebugModeError
(
Exception
):
pass
class
BadClinkerOutput
(
DebugModeError
):
"""Exception: a c implementation and python implementation don't agree"""
r
=
None
"""TODO"""
a
=
None
"""TODO"""
b
=
None
"""TODO"""
def
__init__
(
self
,
r
,
a
,
b
):
"""Initialize members"""
super
(
BadClinkerOutput
,
self
)
.
__init__
()
self
.
r
=
r
self
.
a
=
a
self
.
b
=
b
class
BadOptimization
(
DebugModeError
):
"""Exception: some result and its substitute take different runtime values."""
new_r
=
None
"""TODO"""
r_val
=
None
"""TODO"""
new_r_val
=
None
"""TODO"""
reasons
=
[]
"""TODO"""
snapshots
=
[]
"""TODO"""
def
__init__
(
self
,
new_r
,
r_val
,
new_r_val
,
reasons
,
snapshots
):
"""Initialize members"""
super
(
BadOptimization
,
self
)
.
__init__
()
self
.
new_r
=
new_r
self
.
r_val
=
r_val
self
.
new_r_val
=
new_r_val
self
.
reasons
=
reasons
self
.
snapshots
=
snapshots
#def __str__(self):
#return self.str_diagnostic() #debatable...
def
str_diagnostic
(
self
):
"""TODO: what does this mean? How to interpret? """
sio
=
StringIO
()
print
>>
sio
,
" Result:"
,
id
(
self
.
new_r
),
self
.
new_r
print
>>
sio
,
" Op"
,
self
.
new_r
.
owner
print
>>
sio
,
" Value Type:"
,
type
(
self
.
new_r_val
)
print
>>
sio
,
" Old Value: "
,
self
.
r_val
print
>>
sio
,
" Value: "
,
self
.
new_r_val
print
>>
sio
,
" Reason: "
,
[(
str
(
reason
),
id
(
old_r
))
for
reason
,
old_r
in
self
.
reasons
[
self
.
new_r
]]
print
>>
sio
,
" Snapshots:"
for
s
in
self
.
snapshots
[
self
.
new_r
]:
print
>>
sio
,
" BEFORE"
print
>>
sio
,
s
[
1
]
print
>>
sio
,
" AFTER"
print
>>
sio
,
s
[
2
]
return
sio
.
getvalue
()
def
debugprint
(
a
,
prefix
=
''
,
depth
=-
1
,
done
=
None
,
file
=
sys
.
stdout
):
if
depth
==
0
:
return
...
...
@@ -194,7 +264,21 @@ def optcheck_env(input_specs, output_specs, accept_inplace = False):
env
.
extend
(
Supervisor
(
input
for
spec
,
input
in
zip
(
input_specs
,
inputs
)
if
not
(
spec
.
mutable
or
(
hasattr
(
env
,
'destroyers'
)
and
env
.
destroyers
(
input
)))))
return
env
,
map
(
SymbolicOutput
,
updates
),
equivalence_tracker
class
OptCheckLinker
(
OpWiseCLinker
):
class
DebugModeLinker
(
gof
.
link
.
LocalLinker
):
def
__init__
(
self
,
maker
):
super
(
gof
.
LocalLinker
,
self
)
.
__init__
()
self
.
env
=
None
self
.
maker
=
maker
def
accept
(
self
,
env
,
no_recycling
=
[]):
if
self
.
env
is
not
None
and
self
.
env
is
not
env
:
assert
type
(
self
)
is
DebugModeLinker
return
type
(
self
)(
self
.
env
,
self
.
maker
)
.
accept
(
env
,
no_recycling
)
self
.
env
=
env
self
.
no_recycling
=
no_recycling
return
self
def
make_all
(
self
,
profiler
=
None
,
input_storage
=
None
,
output_storage
=
None
):
env
=
self
.
env
#order = env.toposort()
...
...
@@ -207,19 +291,20 @@ class OptCheckLinker(OpWiseCLinker):
order_outputs
.
reverse
()
order
=
graph
.
io_toposort
(
env
.
inputs
,
order_outputs
)
no_recycling
=
self
.
no_recycling
input_storage
,
output_storage
,
storage_map
=
link
.
map_storage
(
env
,
order
,
input_storage
,
output_storage
)
thunks
=
[]
thunks_py
=
[]
#python thunks
thunks_c
=
[]
#c thunks
for
node
in
order
:
node_input_storage
=
[
storage_map
[
r
]
for
r
in
node
.
inputs
]
node_output_storage
=
[
storage_map
[
r
]
for
r
in
node
.
outputs
]
try
:
raise
NotImplementedError
(
'need to copy destroyed inputs'
)
if
not
self
.
maker
.
mode
.
check_c_code
:
raise
utils
.
AbstractFunctionError
()
e
=
Env
(
*
graph
.
clone
(
node
.
inputs
,
node
.
outputs
))
e
.
toposort
=
lambda
:
e
.
nodes
e
.
toposort
=
lambda
:
e
.
nodes
#WARNING: STOCHASTIC ORDER
if
any
(
isinstance
(
input
,
graph
.
Value
)
for
input
in
node
.
inputs
):
desc
=
None
...
...
@@ -248,18 +333,18 @@ class OptCheckLinker(OpWiseCLinker):
output_storage
=
node_output_storage
)
thunk
.
inputs
=
node_input_storage
thunk
.
outputs
=
node_output_storage
thunks
.
append
(
thunk
)
except
(
NotImplementedError
,
utils
.
AbstractFunctionError
)
:
if
self
.
fallback_on_perform
:
p
=
node
.
op
.
perform
thunk
=
(
lambda
p
=
p
,
i
=
node_input_storage
,
o
=
node_output_storage
,
n
=
node
:
p
(
n
,
[
copy
.
copy
(
x
[
0
])
for
x
in
i
],
o
))
thunk
.
inputs
=
node_input_storage
thunk
.
outputs
=
node_output_storage
thunk
.
perform
=
p
thunks
.
append
(
thunk
)
else
:
raise
thunks
_c
.
append
(
thunk
)
except
utils
.
AbstractFunctionError
:
thunks_c
.
append
(
None
)
p
=
node
.
op
.
perform
thunk
=
(
lambda
p
=
p
,
i
=
node_input_storage
,
o
=
node_output_storage
,
n
=
node
:
p
(
n
,
[
x
[
0
]
for
x
in
i
],
o
))
thunk
.
inputs
=
node_input_storage
thunk
.
outputs
=
node_output_storage
thunk
.
perform
=
p
thunks_py
.
append
(
thunk
)
if
no_recycling
is
True
:
no_recycling
=
storage_map
.
values
()
...
...
@@ -267,97 +352,102 @@ class OptCheckLinker(OpWiseCLinker):
else
:
no_recycling
=
[
storage_map
[
r
]
for
r
in
no_recycling
if
r
not
in
env
.
inputs
]
#####
# This is the function that runs when you evaluate the graph
#####
def
f
():
for
x
in
no_recycling
:
x
[
0
]
=
None
try
:
equiv_vals
=
{}
problematic
=
set
()
r_vals
=
{}
assert
len
(
thunks
)
==
len
(
order
)
for
r
in
env
.
inputs
:
equiv_vals
=
{}
problematic
=
set
()
# r_vals are the true values associated with each result in the graph
# they should not change during 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
r_vals
=
{}
assert
len
(
thunks_py
)
==
len
(
order
)
#put the initial values into the r_vals
for
r
in
storage_map
:
if
storage_map
[
r
][
0
]
is
not
None
:
r_vals
[
r
]
=
copy
.
copy
(
storage_map
[
r
][
0
])
storage_map
[
r
][
0
]
=
None
try
:
# compute the value of all results
for
i
,
(
thunk
,
node
)
in
enumerate
(
zip
(
thunks
,
order
)):
thunk
()
for
i
,
(
thunk_py
,
thunk_c
,
node
)
in
enumerate
(
zip
(
thunks_py
,
thunks_c
,
order
)):
#put a copy of each input into the storage_map
for
r
in
node
.
inputs
:
storage_map
[
r
][
0
]
=
copy
.
copy
(
r_vals
[
r
])
thunk_py
()
#retrieve a copy of each output from the storage_map
for
r
in
node
.
outputs
:
assert
r
not
in
r_vals
this_r_val
=
copy
.
copy
(
storage_map
[
r
][
0
])
r_vals
[
r
]
=
this_r_val
# iterate over results looking for values that don't match the values of the
# results they replaced. This is the sign of a broken optimization.
for
i
,
(
thunk
,
node
)
in
enumerate
(
zip
(
thunks
,
order
)):
for
new_r
in
node
.
outputs
:
for
reason
,
r
in
env
.
equivalence_tracker
.
reasons
[
new_r
]:
problem
=
False
#check if the value for new_r doesn't match the value for r
new_r_val
=
r_vals
[
new_r
]
r_val
=
r_vals
[
r
]
if
type
(
new_r_val
)
!=
type
(
r_val
):
problem
=
True
elif
type
(
new_r_val
)
is
numpy
.
ndarray
:
if
not
numpy
.
allclose
(
new_r_val
,
r_val
):
problem
=
True
else
:
print
>>
sys
.
stderr
,
'WARNING: OptCheck comparison of'
,
type
(
new_r_val
),
'NotImplementedError'
if
problem
:
print
"OPTCHECK FAILURE"
print
" Result:"
,
id
(
new_r
),
new_r
print
" Op"
,
new_r
.
owner
print
" Value Type:"
,
type
(
new_r_val
)
print
" Old Value: "
,
r_val
print
" Value: "
,
new_r_val
print
" Reason: "
,
[(
str
(
reason
),
id
(
old_r
))
for
reason
,
old_r
in
env
.
equivalence_tracker
.
reasons
[
new_r
]]
print
" Snapshots:"
for
s
in
env
.
equivalence_tracker
.
snapshots
[
new_r
]:
print
" BEFORE"
print
s
[
1
]
print
" AFTER"
print
s
[
2
]
print
""
# There is no point in continuing to check for more problems,
# because the incorrect result detected here will cause
# subsequent outputs to be incorrect.
raise
Exception
(
"OptCheckFailure"
)
if
0
:
#OLD CODE
#print out the summary of the first problematic equivalence group
min_member
=
[]
for
problem_r
in
problematic
:
problem_r_set
=
env
.
equivalence_tracker
.
equiv
[
problem_r
]
for
i
,
n
in
enumerate
(
order
):
if
problem_r_set
.
intersection
(
n
.
outputs
):
break
min_member
.
append
((
i
,
problem_r_set
))
min_member
.
sort
()
problematic_set
=
min_member
[
0
][
1
]
if
r
in
r_vals
:
# r has been constant-folded
if
not
r
.
type
.
values_eq_enough
(
r_vals
[
r
],
storage_map
[
r
][
0
]):
raise
Exception
(
'BadConstantFold'
,
(
r
,
r_vals
[
r
],
storage_map
[
r
][
0
]))
#TODO: make a proper exception class for this
else
:
r_vals
[
r
]
=
copy
.
copy
(
storage_map
[
r
][
0
])
if
thunk_c
:
for
r
in
node
.
outputs
:
storage_map
[
r
][
0
]
=
None
#clear the storage_map for the thunk_c
if
0
:
# TODO: check that Op didn't change any inputs that it wasn't allowed to
# (Hint: use the destroy_map attribute)
raise
NotImplementedError
()
else
:
for
r
in
node
.
inputs
:
storage_map
[
r
][
0
]
=
copy
.
copy
(
r_vals
[
r
])
thunk_c
()
for
r
in
node
.
outputs
:
# compares the version from thunk_py (in r_vals)
# to the version produced by thunk_c (in storage_map)
if
not
r
.
type
.
values_eq_enough
(
r_vals
[
r
],
storage_map
[
r
][
0
]):
raise
BadClinkerOutput
(
r
,
r_vals
[
r
],
storage_map
[
r
][
0
])
except
:
raise_with_op
(
node
)
f
.
allow_gc
=
self
.
allow_gc
# iterate over results looking for values that don't match the values of the
# results they replaced. This is the sign of a broken optimization.
for
i
,
node
in
enumerate
(
order
):
for
new_r
in
node
.
outputs
:
for
reason
,
r
in
env
.
equivalence_tracker
.
reasons
[
new_r
]:
problem
=
False
#check if the value for new_r doesn't match the value for r
new_r_val
=
r_vals
[
new_r
]
r_val
=
r_vals
[
r
]
assert
r
.
type
==
new_r
.
type
if
not
r
.
type
.
values_eq_enough
(
r_val
,
new_r_val
):
raise
BadOptimization
(
new_r
,
r_val
,
new_r_val
,
env
.
equivalence_tracker
.
reasons
,
env
.
equivalence_tracker
.
snapshots
)
f
.
allow_gc
=
True
return
f
,
[
link
.
Container
(
input
,
storage
)
for
input
,
storage
in
zip
(
env
.
inputs
,
input_storage
)],
\
[
link
.
Container
(
output
,
storage
,
True
)
for
output
,
storage
in
zip
(
env
.
outputs
,
output_storage
)],
\
thunks
,
order
thunks
_py
,
order
NODEFAULT
=
[
'NODEFAULT'
]
class
OptCheckFunctionMaker
(
FunctionMaker
):
class
DebugModeFunctionMaker
(
FunctionMaker
):
#inheritance buys a few helper functions
verbose
=
0
"""Verbosity level of compile-time and run-time checks. (Default 0: silent)"""
def
__init__
(
self
,
inputs
,
outputs
,
optimizer
,
chances_for_optimizer_to_screw_up
=
10
,
def
__init__
(
self
,
inputs
,
outputs
,
optimizer
,
mode
,
accept_inplace
=
False
,
function_builder
=
Function
):
"""
...
...
@@ -372,7 +462,6 @@ class OptCheckFunctionMaker(FunctionMaker):
in the graph from the inputs to the outputs
"""
# Handle the case where inputs and/or outputs is a single Result (not in a list)
unpack_single
=
False
if
not
isinstance
(
outputs
,
(
list
,
tuple
)):
...
...
@@ -388,7 +477,7 @@ class OptCheckFunctionMaker(FunctionMaker):
expanded_inputs
=
reduce
(
list
.
__add__
,
[
list
(
z
)
for
x
,
y
,
z
in
indices
],
[])
# make the env
for
i
in
xrange
(
chances_for_optimizer_to_screw_up
):
for
i
in
xrange
(
mode
.
stability_patience
):
env
,
additional_outputs
,
equivalence_tracker
=
optcheck_env
(
expanded_inputs
,
outputs
,
accept_inplace
)
env
.
equivalence_tracker
=
equivalence_tracker
# optimize the env
...
...
@@ -410,7 +499,8 @@ class OptCheckFunctionMaker(FunctionMaker):
sys
.
exit
(
1
)
break
else
:
print
>>
sys
.
stdout
,
"OPTCHECK: optimization"
,
i
,
"of"
,
len
(
li
),
"events was stable."
if
self
.
verbose
:
print
>>
sys
.
stdout
,
"OPTCHECK: optimization"
,
i
,
"of"
,
len
(
li
),
"events was stable."
else
:
env0
=
env
...
...
@@ -419,7 +509,7 @@ class OptCheckFunctionMaker(FunctionMaker):
self
.
env
=
env
#equivalence_tracker.printstuff()
linker
=
OptCheckLinker
(
)
linker
=
DebugModeLinker
(
self
)
#the 'no_borrow' outputs are the ones for which that we can't return the internal storage pointer.
...
...
@@ -436,6 +526,7 @@ class OptCheckFunctionMaker(FunctionMaker):
self
.
unpack_single
=
unpack_single
self
.
accept_inplace
=
accept_inplace
self
.
function_builder
=
function_builder
self
.
mode
=
mode
def
create
(
self
,
defaults
=
None
,
trustme
=
False
):
"""
...
...
@@ -531,7 +622,7 @@ class OptCheckFunctionMaker(FunctionMaker):
fn
=
self
.
function_builder
(
_fn
,
_i
,
_o
,
self
.
indices
,
self
.
outputs
,
defaults
,
self
.
unpack_single
,
self
)
return
fn
class
OptCheck
(
Mode
):
class
DebugMode
(
Mode
):
"""Evaluation Mode that detects optimization errors.
A basic premise of how theano works is that every node that is replaced during optimization should compute the same thing as its replacement.
...
...
@@ -547,14 +638,16 @@ class OptCheck(Mode):
# function_module.function
def
function_maker
(
self
,
i
,
o
,
m
,
*
args
,
**
kwargs
):
assert
m
is
self
return
OptCheckFunctionMaker
(
i
,
o
,
self
.
optimizer
,
chances_for_optimizer_to_screw_up
=
self
.
stability_patience
,
*
args
,
**
kwargs
)
def
__init__
(
self
,
optimizer
=
'fast_run'
,
stability_patience
=
10
):
super
(
OptCheck
,
self
)
.
__init__
(
return
DebugModeFunctionMaker
(
i
,
o
,
self
.
optimizer
,
self
,
*
args
,
**
kwargs
)
def
__init__
(
self
,
optimizer
=
'fast_run'
,
stability_patience
=
10
,
check_c_code
=
True
):
super
(
DebugMode
,
self
)
.
__init__
(
optimizer
=
optimizer
,
linker
=
OptCheck
Linker
)
linker
=
DebugMode
Linker
)
self
.
stability_patience
=
stability_patience
self
.
check_c_code
=
check_c_code
theano/sandbox/test_debugmode.py
浏览文件 @
8e9592b7
import
numpy
import
scipy.sparse
from
theano
import
gof
import
theano.sparse
import
theano
import
theano.tensor
import
debugmode
import
theano.compile
def
test0
():
x
=
theano
.
tensor
.
dvector
()
f
=
theano
.
function
([
x
],
(
2.
*
x
+
7
)
/
2.
,
mode
=
debugmode
.
OptCheck
())
f
=
theano
.
function
([
x
],
(
2.
*
x
+
7
)
/
2.
,
mode
=
debugmode
.
DebugMode
())
print
f
([
1
,
2
])
class
BROKEN_ON_PURPOSE_StructuredDotCSC
(
gof
.
Op
):
def
__init__
(
self
,
py_offset
):
gof
.
Op
.
__init__
(
self
)
self
.
py_offset
=
py_offset
def
__eq__
(
self
,
other
):
return
type
(
self
)
==
type
(
other
)
and
(
self
.
py_offset
==
other
.
py_offset
)
def
__hash__
(
self
):
return
29834
^
hash
(
type
(
self
))
^
hash
(
self
.
py_offset
)
def
make_node
(
self
,
a_val
,
a_ind
,
a_ptr
,
a_nrows
,
b
):
a_nrows
=
theano
.
tensor
.
as_tensor
(
a_nrows
)
assert
a_val
.
type
.
dtype
==
b
.
type
.
dtype
r
=
gof
.
Apply
(
self
,
[
a_val
,
a_ind
,
a_ptr
,
a_nrows
,
b
],
[
theano
.
tensor
.
tensor
(
a_val
.
type
.
dtype
,
(
False
,
False
))])
return
r
def
perform
(
self
,
node
,
(
a_val
,
a_ind
,
a_ptr
,
a_nrows
,
b
),
(
out
,)):
a
=
scipy
.
sparse
.
csc_matrix
((
a_val
,
a_ind
,
a_ptr
),
(
a_nrows
,
b
.
shape
[
0
]),
copy
=
False
)
# TODO: todense() is automatic in 0.7.0, just remove the following line:
# out[0] = numpy.asarray(a.dot(b).todense())
out
[
0
]
=
a
.
dot
(
b
)
+
0.5
if
self
.
py_offset
else
a
.
dot
(
b
)
#ERROR TO ADD THIS CRAPPY OFFSET
#assert _is_dense(out[0])
def
c_code
(
self
,
node
,
name
,
(
a_val
,
a_ind
,
a_ptr
,
a_nrows
,
b
),
(
z
,),
sub
):
return
"""
if (
%(a_val)
s->nd != 1) {PyErr_SetString(PyExc_NotImplementedError, "rank(a_val) != 1");
%(fail)
s;}
if (
%(a_ind)
s->nd != 1) {PyErr_SetString(PyExc_NotImplementedError, "rank(a_ind) != 1");
%(fail)
s;}
if (
%(a_ptr)
s->nd != 1) {PyErr_SetString(PyExc_NotImplementedError, "rank(a_ptr) != 1");
%(fail)
s;}
if (
%(a_nrows)
s->nd != 0) {PyErr_SetString(PyExc_NotImplementedError, "rank(nrows) != 0");
%(fail)
s;}
if (
%(b)
s->nd != 2) {PyErr_SetString(PyExc_NotImplementedError, "rank(b) != 2");
%(fail)
s;}
if (
%(a_val)
s->descr->type_num != PyArray_DOUBLE)
{PyErr_SetString(PyExc_NotImplementedError, "a_val dtype not NPY_DOUBLE");
%(fail)
s;}
if (
%(a_ind)
s->descr->type_num != PyArray_INT32) {
PyErr_SetString(PyExc_NotImplementedError, "a_ind dtype not INT32");
%(fail)
s;}
if (
%(a_ptr)
s->descr->type_num != PyArray_INT32)
{PyErr_SetString(PyExc_NotImplementedError, "a_ptr dtype not INT32");
%(fail)
s;}
if (
%(a_nrows)
s->descr->type_num != PyArray_INT32)
{PyErr_SetString(PyExc_NotImplementedError, "a_nrows dtype not INT32");
%(fail)
s;}
if (
%(b)
s->descr->type_num != PyArray_DOUBLE)
{PyErr_SetString(PyExc_NotImplementedError, "b's dtype not NPY_DOUBLE");
%(fail)
s;}
if (
%(a_val)
s->dimensions[0] !=
%(a_ind)
s->dimensions[0])
{PyErr_SetString(PyExc_NotImplementedError, "a_val and a_ind have different lengths");
%(fail)
s;}
if (
%(a_ptr)
s->dimensions[0] !=
%(b)
s->dimensions[0]+1)
{PyErr_SetString(PyExc_NotImplementedError, "a's number of columns doesn't match b's rows");
%(fail)
s;}
if ((!
%(z)
s)
|| (
%(z)
s->dimensions[0] != ((npy_int32 *)
%(a_nrows)
s->data)[0])
|| (
%(z)
s->dimensions[1] !=
%(b)
s->dimensions[1])
)
{
if (
%(z)
s) Py_DECREF(
%(z)
s);
npy_intp dims[] = {0,0};
dims[0] = ((npy_int32 *)
%(a_nrows)
s->data)[0];
dims[1] =
%(b)
s->dimensions[1];
%(z)
s = (PyArrayObject*) PyArray_SimpleNew(2, dims,
%(b)
s->descr->type_num);
}
{
//the output array has size M x N
npy_intp M =
%(z)
s->dimensions[0];
npy_intp N =
%(z)
s->dimensions[1];
npy_intp K =
%(b)
s->dimensions[0];
npy_intp Szm =
%(z)
s->strides[0] /
%(z)
s->descr->elsize;
npy_intp Szn =
%(z)
s->strides[1] /
%(z)
s->descr->elsize;
//npy_intp Sbm =
%(b)
s->strides[0] /
%(b)
s->descr->elsize;
npy_intp Sbn =
%(b)
s->strides[1] /
%(b)
s->descr->elsize;
npy_intp Sval =
%(a_val)
s->strides[0] /
%(a_val)
s->descr->elsize;
npy_intp Sind =
%(a_ind)
s->strides[0] /
%(a_ind)
s->descr->elsize;
npy_intp Sptr =
%(a_ptr)
s->strides[0] /
%(a_ptr)
s->descr->elsize;
npy_double * __restrict__ Dz = (npy_double*)
%(z)
s->data;
//const npy_double * __restrict__ Db = (npy_double*)
%(b)
s->data;
const npy_double * __restrict__ Dval = (npy_double*)
%(a_val)
s->data;
const npy_int32 * __restrict__ Dind = (npy_int32*)
%(a_ind)
s->data;
const npy_int32 * __restrict__ Dptr = (npy_int32*)
%(a_ptr)
s->data;
//npy_intp nnz =
%(a_ind)
s->dimensions[0];
//clear the output array
for (npy_intp m = 0; m < M; ++m)
{
for (npy_intp n = 0; n < N; ++n)
{
//Dz[m*Szm + n*Szn] = 0.0;
Dz[m*Szm + n*Szn] = 0.5; //here is the py_offset amount
}
}
//iterate over the sparse array, making the most of an entry wherever we find it.
//
// Normal matrix matrix multiply:
// for m
// for n
// for k
// z[m,n] += a[m,k] * b[k,n]
// Here instead:
// for k
// for m (sparse)
// for n
// z[m,n] += a[m,k] * b[k,n]
for (npy_int32 k = 0; k < K; ++k)
{
const npy_double * __restrict__ bk = (double *)(
%(b)
s->data +
%(b)
s->strides[0] * k);
for (npy_int32 m_idx = Dptr[k * Sptr]; m_idx < Dptr[(k+1) * Sptr]; ++m_idx)
{
npy_int32 m = Dind[m_idx * Sind];
const double Amk = Dval[m_idx * Sval];
npy_double * __restrict__ zm = (npy_double *)(
%(z)
s->data +
%(z)
s->strides[0] * m);
if (m >=
%(z)
s->dimensions[0])
{PyErr_SetString(PyExc_NotImplementedError, "illegal row index in a");
%(fail)
s;}
for(npy_int32 n = 0; n < N; ++n)
{
zm[n*Szn] += Amk * bk[n*Sbn];
}
}
}
}
"""
%
dict
(
locals
(),
**
sub
)
# inconsistent is a invalid op, whose perform and c_code do not match
inconsistent
=
BROKEN_ON_PURPOSE_StructuredDotCSC
(
False
)
# off_by_half is a good op, that is different from theano.sparse.sd_csc
off_by_half
=
BROKEN_ON_PURPOSE_StructuredDotCSC
(
True
)
def
test_badclinkeroutput
():
vals
=
theano
.
tensor
.
dvector
()
inds
=
theano
.
tensor
.
ivector
()
ptrs
=
theano
.
tensor
.
ivector
()
nrows
=
theano
.
tensor
.
iscalar
()
b
=
theano
.
tensor
.
dmatrix
()
f_good
=
theano
.
function
([
vals
,
inds
,
ptrs
,
nrows
,
b
],
theano
.
sparse
.
StructuredDotCSC
()(
vals
,
inds
,
ptrs
,
nrows
,
b
),
mode
=
debugmode
.
DebugMode
(
check_c_code
=
True
))
f_inconsistent
=
theano
.
function
([
vals
,
inds
,
ptrs
,
nrows
,
b
],
inconsistent
(
vals
,
inds
,
ptrs
,
nrows
,
b
),
mode
=
debugmode
.
DebugMode
(
check_c_code
=
True
))
#this should evaluate with no error
rval_good
=
f_good
([
1.0
,
2.0
,
3.0
],
[
0
,
1
,
2
],
[
0
,
1
,
2
,
3
],
3
,
numpy
.
asarray
([[
0.
,
1.
,
2.
],[
3.
,
4.
,
5.
],[
6.
,
7.
,
8.
]]))
try
:
rval
=
f_inconsistent
([
1.0
,
2.0
,
3.0
],
[
0
,
1
,
2
],
[
0
,
1
,
2
,
3
],
3
,
numpy
.
asarray
([[
0.
,
1.
,
2.
],[
3.
,
4.
,
5.
],[
6.
,
7.
,
8.
]]))
except
debugmode
.
BadClinkerOutput
,
e
:
print
repr
(
e
)
assert
e
.
r
.
owner
.
op
is
inconsistent
return
#TEST PASS
assert
False
#an error should have been detected
def
test_badoptimization
():
@gof.local_optimizer
([
theano
.
sparse
.
sd_csc
])
def
insert_broken_csc
(
node
):
if
node
.
op
==
theano
.
sparse
.
sd_csc
:
return
[
off_by_half
(
*
node
.
inputs
)]
return
False
edb
=
gof
.
EquilibriumDB
()
edb
.
register
(
'insert_broken_csc'
,
insert_broken_csc
,
'all'
)
opt
=
edb
.
query
(
'+all'
)
vals
=
theano
.
tensor
.
dvector
()
inds
=
theano
.
tensor
.
ivector
()
ptrs
=
theano
.
tensor
.
ivector
()
nrows
=
theano
.
tensor
.
iscalar
()
b
=
theano
.
tensor
.
dmatrix
()
f
=
theano
.
function
([
vals
,
inds
,
ptrs
,
nrows
,
b
],
theano
.
sparse
.
sd_csc
(
vals
,
inds
,
ptrs
,
nrows
,
b
),
mode
=
debugmode
.
DebugMode
(
optimizer
=
opt
,
check_c_code
=
True
))
try
:
rval
=
f
([
1.0
,
2.0
,
3.0
],
[
0
,
1
,
2
],
[
0
,
1
,
2
,
3
],
3
,
numpy
.
asarray
([[
0.
,
1.
,
2.
],[
3.
,
4.
,
5.
],[
6.
,
7.
,
8.
]]))
except
debugmode
.
BadOptimization
,
e
:
assert
str
(
e
.
reasons
[
e
.
new_r
][
0
][
0
])
==
'insert_broken_csc'
return
#TEST PASS
assert
False
theano/tensor/basic.py
浏览文件 @
8e9592b7
...
...
@@ -222,6 +222,10 @@ class Tensor(Type):
"""Compare True iff other is the same kind of Tensor"""
return
type
(
self
)
==
type
(
other
)
and
other
.
dtype
==
self
.
dtype
and
other
.
broadcastable
==
self
.
broadcastable
def
values_eq_enough
(
self
,
a
,
b
):
return
type
(
a
)
is
numpy
.
ndarray
and
type
(
b
)
is
numpy
.
ndarray
\
and
(
a
.
shape
==
b
.
shape
)
and
numpy
.
allclose
(
a
,
b
)
def
__hash__
(
self
):
"""Hash equal for same kinds of Tensor"""
return
hash
(
self
.
dtype
)
^
hash
(
self
.
broadcastable
)
...
...
theano/tensor/tests/test_opt.py
浏览文件 @
8e9592b7
...
...
@@ -13,7 +13,7 @@ from theano import pprint
import
numpy
#import scalar_opt
from
theano.sandbox.debugmode
import
OptCheck
from
theano.sandbox.debugmode
import
DebugMode
from
theano
import
function
...
...
@@ -136,7 +136,7 @@ class test_greedy_distribute(unittest.TestCase):
,
eps
+
y
/
s
,
s
)
f
=
function
([
s
,
eps
,
x
,
y
],
r
**
2
,
mode
=
OptCheck
())
f
=
function
([
s
,
eps
,
x
,
y
],
r
**
2
,
mode
=
DebugMode
())
r0
=
f
(
4
,
1.e-6
,
[
1.5
,
2
],
[
2.3
,
3.1
])
r1
=
f
(
4
,
1.e-6
,
[
1.5
,
2
],
[
2.3
,
3.1
])
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论