Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
P
pytensor
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
testgroup
pytensor
Commits
a9acfba5
提交
a9acfba5
authored
12月 18, 2014
作者:
Arnaud Bergeron
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Rework COp to allow all methods to be defined.
上级
0056cb74
隐藏空白字符变更
内嵌
并排
正在显示
2 个修改的文件
包含
353 行增加
和
259 行删除
+353
-259
extending_theano_c.txt
doc/tutorial/extending_theano_c.txt
+123
-99
op.py
theano/gof/op.py
+230
-160
没有找到文件。
doc/tutorial/extending_theano_c.txt
浏览文件 @
a9acfba5
...
@@ -688,14 +688,13 @@ To help with this, Theano defines a class, ``COp``, from which new C ops
...
@@ -688,14 +688,13 @@ To help with this, Theano defines a class, ``COp``, from which new C ops
can inherit. The class ``COp`` aims to simplify the process of implementing
can inherit. The class ``COp`` aims to simplify the process of implementing
C ops by doing the following :
C ops by doing the following :
*
It allows you to define the C implementation of your op in a distinct
* It allows you to define the C implementation of your op in a distinct
C code file. This makes it easier to keep your Python and C code
C code file. This makes it easier to keep your Python and C code
readable and well indented.
readable and well indented.
* It automatically handles the methods :meth:`Op.c_code()`,
* It can automatically handle all the methods that return C code,
:meth:`Op.c_support_code()`, :meth:`Op.c_support_code_apply()` and
in addition to :meth:`Op.c_code_cache_version()` based on the
:meth:`Op.c_code_cache_version()` based on the provided external C
provided external C implementation.
implementation.
To illustrate how much simpler the class ``COp`` makes the process of defining
To illustrate how much simpler the class ``COp`` makes the process of defining
a new op with a C implementation, let's revisit the second example of this
a new op with a C implementation, let's revisit the second example of this
...
@@ -740,7 +739,7 @@ C file named vectorTimesVector.c :
...
@@ -740,7 +739,7 @@ C file named vectorTimesVector.c :
.. code-block:: c
.. code-block:: c
THEANO_SUPPORT_CODE_SECTION
#section support_code
// Support code function
// Support code function
bool vector_same_shape(PyArrayObject* arr1, PyArrayObject* arr2)
bool vector_same_shape(PyArrayObject* arr1, PyArrayObject* arr2)
...
@@ -749,7 +748,7 @@ C file named vectorTimesVector.c :
...
@@ -749,7 +748,7 @@ C file named vectorTimesVector.c :
}
}
THEANO_APPLY_CODE_SECTION
#section support_code_apply
// Apply-specific support function
// Apply-specific support function
void APPLY_SPECIFIC(vector_elemwise_mult)(
void APPLY_SPECIFIC(vector_elemwise_mult)(
...
@@ -822,45 +821,44 @@ this new version of the VectorTimesVector op :
...
@@ -822,45 +821,44 @@ this new version of the VectorTimesVector op :
* Parent class : instead of inheriting from the class :class:`Op`,
* Parent class : instead of inheriting from the class :class:`Op`,
VectorTimesVector inherits from the class ``COp``.
VectorTimesVector inherits from the class ``COp``.
* Constructor : in our new op, the ``__init__()`` method has an
important
* Constructor : in our new op, the ``__init__()`` method has an
use; to inform the constructor of the ``COp`` class of the location,
important use; to inform the constructor of the ``COp`` class
o
n the filesystem of the C implementation of this op. To do this, it
o
f the location, on the filesystem of the C implementation of
gives the path of file containing the C code as well as the name of
this op. To do this, it gives a list of file paths containing
the
function, in that file, that should be called to perform the
the
C code for this op. To auto-generate the c_code method
computation. The path should be given as a relative path from
the
with a function call you can specify the function name as
the
folder where the descendant of the ``COp`` class is defined.
second parameter. The paths should be given as a relative
path from the folder where the descendant of the ``COp`` class
* ``make_node()`` : the ``make_node()`` method is absolutely identical to
is defined.
the one in our old example. Using the ``COp`` class doesn't change
anything here.
* ``make_node()`` : the ``make_node()`` method is absolutely
identical to the one in our old example. Using the ``COp``
* External C code : the external C code performs the computation
class doesn't change anything here.
associated with the op. It contains, at the very least, a 'main' function
having the same name as provided to the constructor of the Python clas
s
* External C code : the external C code implements the variou
s
``COp``. Writing this C code involves a few subtleties which deserve their
functions associated with the op. associated with the op.
own respective sections.
Writing this C code involves a few subtleties which deserve
their own respective sections.
Main function
Main function
-------------
-------------
The external C implementation must implement a main function whose name
If you pass a function name to the ``__init__()`` method of the ``COp`` class, it must respect the following constraints:
is passed by the op to the ``__init__()`` method of the ``COp`` class. This
main C function must respect the following constraints :
* It must return an int. The value of that int indicates whether the
* It must return an int. The value of that int indicates whether
op could perform its task or not. A value of 0 indicates success while
the op could perform its task or not. A value of 0 indicates
any non-zero value will interrupt the execution of the Theano function.
success while any non-zero value will interrupt the execution
Before returning a non-zero integer, the main function should call the
of the Theano function. When returning non-zero the function
function ``PyErr_Format()`` to setup a Python exception.
must set a python exception indicating the details of the
problem.
* It must receive one
pointer
for each input to the op followed by one
* It must receive one
argument
for each input to the op followed by one
pointer to a
pointer
for each output of the op.
pointer to a
n argument
for each output of the op.
For example, the main C function of an op that takes two
scalars as inputs and
For example, the main C function of an op that takes two
arrays as
returns both their sum and the difference between them would have four
inputs and returns both their sum and the difference between them
parameters (two for the op's inputs and two for its outputs) and it'
s
would have four parameters (two for the op's inputs and two for it
s
signature would look something like this :
outputs) and it's
signature would look something like this :
.. code-block:: c
.. code-block:: c
...
@@ -870,11 +868,21 @@ signature would look something like this :
...
@@ -870,11 +868,21 @@ signature would look something like this :
Macros
Macros
------
------
The ``COp`` class defines a number of macros that can you can use in your C
For certain section tags, your C code can benefit from a number of
implementation to make it simpler and more generic.
pre-defined macros. These section tags have no macros: ``init_code``,
``c_support_code``. All other tags will have the support macros
discussed below.
For every input array 'i' (indexed from 0) of the op, the following macros are
* ``APPLY_SPECIFIC(str)`` which will automatically append a name
defined:
unique to the :ref:`Apply node that applies the Op at the end
of the provided ``str``. The use of this macro is discussed
futher below.
For every input which has a :attr:`dtype` attribute (this means
Tensors, and equivalent types on GPU), the following macros will be
defined unless your Op class has an :attr:`Op.check_input` attribute
defined to False. In these descrptions 'i' refers to the position
(indexed from 0) in the input array.
* ``DTYPE_INPUT_{i}`` : NumPy dtype of the data in the array.
* ``DTYPE_INPUT_{i}`` : NumPy dtype of the data in the array.
This is the variable type corresponding to the NumPy dtype, not the
This is the variable type corresponding to the NumPy dtype, not the
...
@@ -889,71 +897,87 @@ defined:
...
@@ -889,71 +897,87 @@ defined:
* ``TYPENUM_INPUT_{i}`` : Typenum of the data in the array
* ``TYPENUM_INPUT_{i}`` : Typenum of the data in the array
* ``ITEMSIZE_INPUT_{i}`` : Size, in bytes, of the elements in the array.
* ``ITEMSIZE_INPUT_{i}`` : Size, in bytes, of the elements in
the array.
In the same way, the macros ``DTYPE_OUTPUT_{i}``,
``ITEMSIZE_OUTPUT_{i}`` and ``TYPENUM_OUTPUT_{i}`` are defined for
every output 'i' of the op.
In the same way, the macros ``DTYPE_OUTPUT_{i}``, ``ITEMSIZE_OUTPUT_{i}`` and
In addition to these macros, the ``init_code_struct``, ``code``, and
``TYPENUM_OUTPUT_{i}`` are defined for every output 'i' of the op.
``code_cleanup`` also have the following macros:
* ``FAIL`` : Code to insert at error points. A python exception
should be set prior to this code. An invocation look like this:
.. code-block:: c
if (error) {
// Set python exception
FAIL
}
The ``COp`` class also defines the macro ``APPLY_SPECIFIC(str)`` which will
You can add a semicolon after the macro if it makes your editor
automatically append the name of the :ref:`Apply node that applies the Op at
happy.
the end of the provided ``str``. The use of this macro is discussed below.
You should be aware, however, that these macros are apply-specific. As such,
* ``CONTEXT`` : Name of the context variable for this node. (only
any function that uses them is considered to contain apply-specific code.
for Ops which have a context, which is discussed elsewhere)
Finally the tag ``code`` and ``code_cleanup`` have macros to
pass the inputs and output names. These are name ``INPUT_{i}`` and
``OUTPUT_{i}`` where `i` is the 0-based index position in the input
and output arrays respectively.
Support code
Support code
------------
------------
The file whose name is provided to the ``COp`` class is not constrained to
Certain section are limited in what you can place in them due to
contain only one function. It can in fact contain many functions, with every
semantic and syntactic restrictions of the C++ language. Most of
function but the main one acting as support code.
these restrictions apply to the tags that end in ``_struct``.
When we defined the VectorTimesVector op without using the ``COp`` class, we
When we defined the VectorTimesVector op without using the ``COp``
had to make a distinction between two types of support_code : the support
class, we had to make a distinction between two types of support_code
code that was apply-specific and the support code that wasn't.
: the support code that was apply-specific and the support code that
The apply-specific code was defined in the ` c_support_code_apply()`` method
wasn't. The apply-specific code was defined in the
and the elements defined in that code (global variables and functions) had to
``c_support_code_apply()`` method and the elements defined in that
include the name of the Apply node in their own names to avoid conflicts
code (global variables and functions) had to include the name of the
between the different versions of the apply-specific code. The code that
Apply node in their own names to avoid conflicts between the different
wasn't apply-specific was simply defined in the ``c_support_code()`` method.
versions of the apply-specific code. The code that wasn't
apply-specific was simply defined in the ``c_support_code()`` method.
When using the ``COp`` class, we still have to make the distinction between
apply-specific and apply-agnostic support code but we express it differently
To make indentifiers that include the :ref:`Apply` node name use the
in the code since it is all defined in the same external C file.
``APPLY_SPECIFIC(str)`` macro. In the above example, this macro is
These two types of support code should each be defined in their own section of
used when defining the functions ``vector_elemwise_mult()`` and
the file, like in the example above. These sections should be delimited by the
markers ``THEANO_SUPPORT_CODE_SECTION`` (to be put on its own line, at the
beginning of the apply-agnostic support code section) and
``THEANO_APPLY_CODE_SECTION`` (to be put on its own line at the beginning of
the apply-specific code section). Moreover, just like in the previous examples
of this tutorial, apply-specific functions and global variables need to
include the name of the :ref:`Apply` node in their names. To achieve this,
the macro ``APPLY_SPECIFIC(str)`` should be used when defining those elements
as well as when referring to them. In the above example, this macro is used
when defining the functions ``vector_elemwise_mult()`` and
``vector_times_vector()`` as well as when calling function
``vector_times_vector()`` as well as when calling function
``vector_elemwise_mult()`` from inside ``vector_times_vector()``.
``vector_elemwise_mult()`` from inside ``vector_times_vector()``.
:note:
When using the ``COp`` class, we still have to make the distinction
between C code for each of the methods of a C class. These sections of
The macro ``APPLY_SPECIFIC(str)`` should only ever be used for
code are separated by ``#section <tag>`` markers. The tag determines
apply-specific code. It should not be used for apply-agnostic code.
the name of the method this C code applies to with the rule that
``<tag>`` applies to `c_<tag>`. Unknown tags are an error and will be
The rules for knowing if a piece of code should be put in the apply-agnostic
reported. Duplicate tags will be merged together in the order the
or the apply-specific support code section of the file are simple. If it uses
appear in the C files.
any of the macros defined by the class ``COp`` then it is apply-specific and
goes in the corresponding section. If it calls any apply-specific code then
The rules for knowing if where a piece of code should be put can be
it is apply-specific. Otherwise, it is apply-agnostic and goes in the
sometimes tricky. The key thing to remember is that things that can
apply-agnostic support code section.
be shared between instances of the op should be apply-agnostic and go
into a section which does not end in ``_apply`` or ``_struct``. The
In the above example, the ``function vector_same_shape()`` is apply-agnostic
distinction of ``_apply`` and ``_struct`` mostly hinghes on how you
because it uses none of the macros defined by the class ``COp`` and it doesn't
want to manange the lifetime of the object. Note that to use an
rely on any apply-specific code. The function ``vector_elemwise_mult()`` is
apply-specific object, you have to be in a apply-specific section, so
apply-specific because it uses the macros defined by ``COp``. Finally, the
some portions of the code that might seem apply-agnostic may still be
function ``vector_times_vector()`` is apply-specific because it uses those
apply-specific because of the data they use (this does not include
same macros and also because it calls ``vector_elemwise_mult()`` which is an
arguments).
apply-specific function.
In the above example, the ``function vector_same_shape()`` is
apply-agnostic because it uses none of the macros defined by the class
``COp`` and it doesn't rely on any apply-specific code. The function
``vector_elemwise_mult()`` is apply-specific because it uses the
macros defined by ``COp``. Finally, the function
``vector_times_vector()`` is apply-specific because it uses those same
macros and also because it calls ``vector_elemwise_mult()`` which is
an apply-specific function.
Final Note
Final Note
==========
==========
...
...
theano/gof/op.py
浏览文件 @
a9acfba5
...
@@ -17,6 +17,7 @@ import logging
...
@@ -17,6 +17,7 @@ import logging
import
numpy
import
numpy
import
os
import
os
import
sys
import
sys
import
re
import
warnings
import
warnings
import
theano
import
theano
...
@@ -973,6 +974,32 @@ int main( int argc, const char* argv[] )
...
@@ -973,6 +974,32 @@ int main( int argc, const char* argv[] )
compute_map
,
no_recycling
)
compute_map
,
no_recycling
)
def
simple_meth
(
tag
):
def
f
(
self
):
if
tag
in
self
.
code_sections
:
return
self
.
code_sections
[
tag
]
else
:
raise
utils
.
MethodNotDefined
(
'c_'
+
tag
,
type
(
self
),
type
(
self
)
.
__name__
)
f
.
__name__
=
'c_'
+
tag
return
f
def
apply_meth
(
tag
):
def
f
(
self
,
node
,
name
):
if
tag
in
self
.
code_sections
:
code
=
self
.
code_sections
[
tag
]
define_macros
,
undef_macros
=
self
.
get_c_macros
(
node
,
name
)
return
os
.
linesep
.
join
([
define_macros
,
code
,
undef_macros
])
else
:
raise
utils
.
MethodNotDefined
(
'c_'
+
tag
,
type
(
self
),
type
(
self
)
.
__name__
)
f
.
__name__
=
'c_'
+
tag
return
f
class
COp
(
Op
):
class
COp
(
Op
):
""" Class to allow an op to have an external C implementation.
""" Class to allow an op to have an external C implementation.
...
@@ -981,118 +1008,85 @@ class COp(Op):
...
@@ -981,118 +1008,85 @@ class COp(Op):
the C implementation and the name of the function, in that file, to call
the C implementation and the name of the function, in that file, to call
to perform the computations for the op.
to perform the computations for the op.
"""
"""
section_re
=
re
.
compile
(
r'^#section ([a-zA-Z0-9_]+)$'
,
re
.
MULTILINE
)
# This is the set of allowed markers
SECTIONS
=
set
([
'init_code'
,
'init_code_apply'
,
'init_code_struct'
,
'support_code'
,
'support_code_apply'
,
'support_code_struct'
,
'cleanup_code_struct'
,
'code'
,
'code_cleanup'
])
def
__init__
(
self
,
func_file
,
func_name
):
@classmethod
def
get_path
(
cls
,
f
):
self
.
func_file
=
func_file
"""
self
.
func_name
=
func_name
Convert a path relative to the location of the class file into
an aboslute path. Paths that are already absolute are passed
# Define the markers that can be used to delimit sections in the
through unchanged.
# external C code
"""
self
.
support_code_marker
=
"THEANO_SUPPORT_CODE_SECTION"
if
not
os
.
path
.
isabs
(
f
):
self
.
apply_code_marker
=
"THEANO_APPLY_CODE_SECTION"
class_file
=
inspect
.
getfile
(
cls
)
self
.
c_code_markers
=
[
self
.
support_code_marker
,
class_dir
=
os
.
path
.
dirname
(
class_file
)
self
.
apply_code_marker
]
f
=
os
.
path
.
realpath
(
os
.
path
.
join
(
class_dir
,
f
))
return
f
# Load the external C code
def
__init__
(
self
,
func_files
,
func_name
=
None
):
try
:
"""
# Attempt to find the file self.func_file in the folder where the
Sections are loaded from files in order with sections in later
# concrete type of the COp instance is defined
files overriding sections in previous files.
"""
# Get the name of the folder where the concrete type of the COp is
if
not
isinstance
(
func_files
,
list
):
# defined
func_files
=
[
func_files
]
path_concrete_type
=
inspect
.
getfile
(
self
.
__class__
)
folder_concrete_type
=
os
.
path
.
dirname
(
path_concrete_type
)
# Try to open the file from there
f
=
open
(
os
.
path
.
join
(
folder_concrete_type
,
self
.
func_file
),
"r"
)
self
.
func_code
=
f
.
read
()
f
.
close
()
except
IOError
:
# Add information to the exception message to inform the user
# on the locations in which the class COp will look for the
# specified file
message
=
(
"The path to the external C implementation should "
"be given as a relative path from the folder "
"where the Op is defined. "
)
# Can't update the exception's message by modifying e.args
# because IOErrors don't use their attribute args to generate
# their error message
e
.
strerror
=
message
+
e
.
strerror
raise
e
# Separate the contents of the file in sections and validate that at
# lest one of the necessary code sections has been defined
self
.
code_sections
=
self
.
parse_external_c_code
(
self
.
func_code
)
if
sum
([
marker
in
self
.
code_sections
.
keys
()
for
marker
in
self
.
c_code_markers
])
==
0
:
raise
(
RuntimeError
,
"The provided C implementation does not "
"define a support code section or a support code apply "
"section."
)
def
parse_external_c_code
(
self
,
code
):
# Obtain the positions of the C code markers used in the C code
positions
=
[(
code
.
index
(
marker
),
marker
)
for
marker
in
self
.
c_code_markers
if
marker
in
code
]
# Go over the markers in their order of occurence and extract
# the C code they concern
positions
.
sort
()
code_sections
=
{}
for
i
in
range
(
len
(
positions
)):
marker_start
,
marker
=
positions
[
i
]
if
i
<
len
(
positions
)
-
1
:
# This is not the last section in the code : extract the code
# between the beginning of the current marker and the
# beginning of the next one.
next_marker_start
=
positions
[
i
+
1
][
0
]
section
=
code
[
marker_start
:
next_marker_start
]
else
:
# This is the last section in the code : extract the remaining
# C code
section
=
code
[
marker_start
:]
cleaned_section
=
section
.
replace
(
marker
,
""
)
self
.
func_files
=
[
self
.
get_path
(
f
)
for
f
in
func_files
]
code_sections
[
marker
]
=
cleaned_section
self
.
func_name
=
func_name
return
code_sections
self
.
load_c_code
()
if
len
(
self
.
code_sections
)
==
0
:
raise
ValueError
(
"No sections where defined in C files"
)
if
self
.
func_name
is
not
None
:
if
'op_code'
in
self
.
code_sections
:
# maybe a warning instead (and clearing the key)
raise
ValueError
(
'Cannot have an "op_code" section and '
'specify the func_name'
)
if
'op_code_cleanup'
in
self
.
code_sections
:
# maybe a warning instead (and clearing the key)
raise
ValueError
(
'Cannot have an "op_code_cleanup" section '
'and specify the func_name'
)
def
load_c_code
(
self
):
self
.
func_codes
=
[]
for
func_file
in
self
.
func_files
:
with
open
(
func_file
,
'r'
)
as
f
:
self
.
func_codes
.
append
(
f
.
read
())
self
.
code_sections
=
dict
()
for
i
,
code
in
enumerate
(
self
.
func_codes
):
split
=
self
.
section_re
.
split
(
code
)
if
split
[
0
]
.
strip
()
!=
''
:
raise
ValueError
(
'Stray code before first #section '
'statement (in file
%
s):
%
s'
%
(
self
.
func_files
[
i
],
split
[
0
]))
n
=
1
while
n
<
len
(
split
):
if
split
[
n
]
not
in
self
.
SECTIONS
:
raise
ValueError
(
"Unknown section type (in file
%
s):
%
s"
%
(
self
.
fun_files
[
i
],
split
[
n
]))
if
split
[
n
]
not
in
self
.
code_sections
:
self
.
code_sections
[
split
[
n
]]
=
""
self
.
code_sections
[
split
[
n
]]
+=
split
[
n
+
1
]
n
+=
2
def
c_code_cache_version
(
self
):
def
c_code_cache_version
(
self
):
return
hash
(
self
.
func_code
)
return
hash
(
tuple
(
self
.
func_codes
))
def
c_support_code
(
self
):
if
self
.
support_code_marker
in
self
.
code_sections
:
return
self
.
code_sections
[
self
.
support_code_marker
]
else
:
raise
utils
.
MethodNotDefined
(
"c_support_code"
,
type
(
self
),
self
.
__class__
.
__name__
)
def
c_support_code_apply
(
self
,
node
,
name
):
if
self
.
apply_code_marker
in
self
.
code_sections
:
apply_code
=
self
.
code_sections
[
self
.
apply_code_marker
]
if
hasattr
(
self
,
'check_inputs'
)
and
self
.
check_inputs
==
False
:
return
apply_code
else
:
define_macros
,
undef_macros
=
self
.
get_c_macros
(
node
,
name
)
return
os
.
linesep
.
join
([
define_macros
,
apply_code
,
undef_macros
])
else
:
raise
utils
.
MethodNotDefined
(
"c_support_code_apply"
,
type
(
self
),
self
.
__class__
.
__name__
)
c_init_code
=
simple_meth
(
'init_code'
)
c_init_code_apply
=
apply_meth
(
'init_code_apply'
)
c_support_code
=
simple_meth
(
'support_code'
)
c_support_code_apply
=
apply_meth
(
'support_code_apply'
)
c_support_code_struct
=
apply_meth
(
'support_code_struct'
)
c_cleanup_code_struct
=
apply_meth
(
'cleanup_code_struct'
)
def
format_c_function_args
(
self
,
inp
,
out
):
def
format_c_function_args
(
self
,
inp
,
out
):
# Generate an string containing the arguments sent to the external C
# Generate an string containing the arguments sent to the external C
...
@@ -1100,73 +1094,149 @@ class COp(Op):
...
@@ -1100,73 +1094,149 @@ class COp(Op):
# "input0, input1, input2, &output0, &output1"
# "input0, input1, input2, &output0, &output1"
return
", "
.
join
(
list
(
inp
)
+
[
"&
%
s"
%
o
for
o
in
out
])
return
", "
.
join
(
list
(
inp
)
+
[
"&
%
s"
%
o
for
o
in
out
])
def
get_c_macros
(
self
,
node
,
name
):
def
get_c_macros
(
self
,
node
,
name
,
check_input
=
None
):
define_template
=
"#define
%
s
%
s"
undef_template
=
"#undef
%
s"
define_macros
=
[]
undef_macros
=
[]
if
check_input
is
None
:
check_input
=
getattr
(
self
,
'check_input'
,
True
)
if
check_input
:
# Extract the various properties of the input and output variables
variables
=
node
.
inputs
+
node
.
outputs
variable_names
=
([
"INPUT_
%
i"
%
i
for
i
in
range
(
len
(
node
.
inputs
))]
+
[
"OUTPUT_
%
i"
%
i
for
i
in
range
(
len
(
node
.
inputs
))])
define_template
=
"#define
%
s
%
s"
+
os
.
linesep
# Generate dtype macros
undef_template
=
"#undef
%
s"
+
os
.
linesep
for
i
,
v
in
enumerate
(
variables
):
define_macros
=
""
if
not
hasattr
(
v
,
'dtype'
):
undef_macros
=
""
continue
vname
=
variable_names
[
i
]
# Extract the various properties of the input and output variables
macro_name
=
"DTYPE_"
+
vname
variables
=
node
.
inputs
+
node
.
outputs
macro_value
=
"npy_"
+
v
.
dtype
variable_names
=
([
"INPUT_
%
i"
%
i
for
i
in
range
(
len
(
node
.
inputs
))]
+
[
"OUTPUT_
%
i"
%
i
for
i
in
range
(
len
(
node
.
inputs
))])
variable_dtypes_names
=
[
v
.
dtype
for
v
in
variables
]
variable_dtypes
=
[
numpy
.
dtype
(
d
)
for
d
in
variable_dtypes_names
]
variable_typenums
=
[
d
.
num
for
d
in
variable_dtypes
]
variable_itemsizes
=
[
d
.
itemsize
for
d
in
variable_dtypes
]
# Generate dtype macros
define_macros
.
append
(
define_template
%
(
macro_name
,
macro_value
))
for
i
in
range
(
len
(
variables
)):
undef_macros
.
append
(
undef_template
%
macro_name
)
macro_name
=
"DTYPE_"
+
variable_names
[
i
]
macro_value
=
"npy_"
+
variable_dtypes_names
[
i
]
define_macros
+=
define_template
%
(
macro_name
,
macro_value
)
d
=
numpy
.
dtype
(
v
.
dtype
)
undef_macros
+=
undef_template
%
macro_name
# Generate typenum macros
macro_name
=
"TYPENUM_"
+
vname
for
i
in
range
(
len
(
variables
)):
macro_value
=
d
.
num
macro_name
=
"TYPENUM_"
+
variable_names
[
i
]
macro_value
=
variable_typenums
[
i
]
define_macros
+=
define_template
%
(
macro_name
,
macro_value
)
define_macros
.
append
(
define_template
%
(
macro_name
,
macro_value
)
)
undef_macros
+=
undef_template
%
macro_name
undef_macros
.
append
(
undef_template
%
macro_name
)
# Generate itemsize macros
macro_name
=
"ITEMSIZE_"
+
vname
for
i
in
range
(
len
(
variables
)):
macro_value
=
d
.
itemsize
macro_name
=
"ITEMSIZE_"
+
variable_names
[
i
]
macro_value
=
variable_itemsizes
[
i
]
define_macros
+=
define_template
%
(
macro_name
,
macro_value
)
define_macros
.
append
(
define_template
%
(
macro_name
,
macro_value
)
)
undef_macros
+=
undef_template
%
macro_name
undef_macros
.
append
(
undef_template
%
macro_name
)
# Generate a macro to mark code as being apply-specific
# Generate a macro to mark code as being apply-specific
define_macros
+=
define_template
%
(
"APPLY_SPECIFIC(str)"
,
define_macros
.
append
(
define_template
%
(
"APPLY_SPECIFIC(str)"
,
"str##_
%
s"
%
name
)
"str##_
%
s"
%
name
))
undef_macros
+=
undef_template
%
"APPLY_SPECIFIC"
undef_macros
.
append
(
undef_template
%
"APPLY_SPECIFIC"
)
return
os
.
linesep
.
join
(
define_macros
),
os
.
linesep
.
join
(
undef_macros
)
def
_lquote_macro
(
self
,
txt
):
res
=
[]
spl
=
txt
.
split
(
'
\n
'
)
for
l
in
spl
[:
-
1
]:
res
.
append
(
l
+
'
\\
'
)
res
.
append
(
spl
[
-
1
])
return
os
.
linesep
.
join
(
res
)
def
get_sub_macros
(
self
,
sub
):
define_macros
=
[]
undef_macros
=
[]
define_macros
.
append
(
"#define FAIL
%
s"
%
(
self
.
_lquote_macro
(
sub
[
'fail'
]),))
undef_macros
.
append
(
"#undef FAIL"
)
if
'context'
in
sub
:
define_macros
.
append
(
"#define CONTEXT
%
s"
%
(
sub
[
'context'
],))
undef_macos
.
append
(
"#undef CONTEXT"
)
return
os
.
linesep
.
join
(
define_macros
),
os
.
linesep
.
join
(
undef_macros
)
def
get_io_macros
(
self
,
inputs
,
outputs
):
define_macros
=
[]
undef_macros
=
[]
for
i
,
inp
in
enumerate
(
inputs
):
define_macros
.
append
(
"#define INPUT_
%
d
%
s"
(
i
,
inp
))
undef_macros
.
append
(
"#undef INPUT_
%
d"
,
(
i
,))
for
i
,
out
in
enumerate
(
outputs
):
define_macros
.
append
(
"#define OUTPUT_
%
d
%
s"
(
i
,
inp
))
undef_macros
.
append
(
"#undef OUTPUT_
%
d"
,
(
i
,))
def
c_init_code_struct
(
self
,
node
,
name
,
sub
):
if
'init_code_struct'
in
self
.
code_sections
:
op_code
=
self
.
code_sections
[
'init_code_struct'
]
def_macros
,
undef_macros
=
self
.
get_c_macros
(
node
,
name
)
def_sub
,
undef_sub
=
self
.
get_sub_macros
(
sub
)
return
os
.
linesep
.
join
([
def_macros
,
def_sub
,
op_code
,
undef_sub
,
undef_macros
])
else
:
raise
utils
.
MethodNotDefined
(
'c_init_code_struct'
,
type
(
self
),
type
(
self
)
.
__name__
)
return
define_macros
,
undef_macros
def
c_code
(
self
,
node
,
name
,
inp
,
out
,
sub
):
def
c_code
(
self
,
node
,
name
,
inp
,
out
,
sub
):
if
self
.
func_name
is
not
None
:
assert
'code'
not
in
self
.
code_sections
func_name
=
self
.
func_name
func_args
=
self
.
format_c_function_args
(
inp
,
out
)
fail
=
sub
[
'fail'
]
define_macros
,
undef_macros
=
self
.
get_c_macros
(
node
,
name
,
check_input
=
False
)
# Generate the C code
return
"""
%(define_macros)
s
{
if (
%(func_name)
s(
%(func_args)
s) != 0) {
%(fail)
s
}
}
%(undef_macros)
s
"""
%
dict
(
func_name
=
self
.
func_name
,
fail
=
sub
[
'fail'
],
func_args
=
self
.
format_c_function_args
(
inp
,
out
),
define_macros
=
define_macros
,
undef_macros
=
undef_macros
)
else
:
if
'code'
in
self
.
code_sections
:
op_code
=
self
.
code_sections
[
'code'
]
func_name
=
self
.
func_name
def_macros
,
undef_macros
=
self
.
get_c_macros
(
node
,
name
)
func_args
=
self
.
format_c_function_args
(
inp
,
out
)
def_sub
,
undef_sub
=
self
.
get_sub_macros
(
sub
)
fail
=
sub
[
'fail'
]
def_io
,
undef_io
=
self
.
get_io_macros
(
inp
,
out
)
# Generate the code to define/undefine the C macros
define_macros
,
undef_macros
=
self
.
get_c_macros
(
node
,
name
)
# Generate the C code
c_code
=
"""
%(define_macros)
s
{
int result =
%(func_name)
s(
%(func_args)
s);
if (result != 0)
{
%(fail)
s;
}
}
%(undef_macros)
s
"""
%
locals
()
return
c_code
return
os
.
linesep
.
join
([
def_macros
,
def_sub
,
def_io
,
op_code
,
undef_io
,
undef_sub
,
undef_macros
])
else
:
raise
utils
.
MethodNotDefined
(
'c_code'
,
type
(
self
),
type
(
self
)
.
__name__
)
def
c_code_cleanup
(
self
,
node
,
name
,
inputs
,
outputs
,
sub
):
if
'code_cleanup'
in
self
.
code_sections
:
op_code
=
self
.
code_sections
[
'code_cleanup'
]
def_macros
,
undef_macros
=
self
.
get_c_macros
(
node
,
name
)
def_sub
,
undef_sub
=
self
.
get_sub_macros
(
sub
)
def_io
,
undef_io
=
self
.
get_io_macros
(
inp
,
out
)
return
os
.
linesep
.
join
([
def_macros
,
def_sub
,
def_io
,
op_code
,
undef_io
,
undef_sub
,
undef_macros
])
else
:
raise
utils
.
MethodNotDefined
(
'c_code_cleanup'
,
type
(
self
),
type
(
self
)
.
__name__
)
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论