提交 8e292493 authored 作者: Frédéric Bastien's avatar Frédéric Bastien 提交者: GitHub

Merge pull request #5612 from notoraptor/make-structs

Implement a struct generator for wrapping op params in Python and C codes.
......@@ -130,6 +130,16 @@ the most important ones:
change in the code. If you don't want to cache the compiled code
return an empty tuple or don't implement it.
.. method:: c_element_type()
Optional: should return the name of the primitive C type of
items into variables handled by this Theano type. For example,
for a matrix of 32-bit signed NumPy integers, it should return
``"npy_int32"``. If C type may change from an instance to another
(e.g. ``Scalar('int32')`` vs ``Scalar('int64')``), consider
implementing this method. If C type is fixed accross instances,
this method may be useless (as you already know the C type
when you work with the C code).
Each of these functions take two arguments, ``name`` and ``sub`` which
must be used to parameterize the C code they return. ``name`` is a
......
......@@ -73,8 +73,10 @@ attribute :attr:`params_type` to an instance of your params Type.
.. note::
If you want to have multiple parameters you have to bundle those
inside a single object and use that as the params type.
If you want to have multiple parameters, Theano provides the convenient class
:class:`theano.gof.params_type.ParamsType` that allows to bundle many parameters into
one object that will be available in both Python (as a Python object) and C code (as a struct).
See :ref:`ParamsType tutorial and API documentation <libdoc_gof_params_type>` for more infos.
For example if we decide to use an int as the params the following
would be appropriate:
......
......@@ -17,4 +17,5 @@
fgraph
toolbox
type
params_type
utils
.. _libdoc_gof_params_type:
============================================================
:mod:`theano.gof.params_type` -- Wrapper class for op params
============================================================
---------
Reference
---------
.. automodule:: theano.gof.params_type
:platform: Unix, Windows
:synopsis: Wrapper class for op params
:members:
.. moduleauthor:: LISA
\ No newline at end of file
......@@ -80,6 +80,8 @@ from theano.gof.type import \
from theano.gof.utils import \
hashtype, object2, MethodNotDefined
from theano.gof.params_type import ParamsType, Params
import theano
if theano.config.cmodule.preload_cache:
......
......@@ -125,9 +125,10 @@ class Apply(Node):
Returns the params for the node, or NoParams if no params is set.
"""
if hasattr(self.op, 'get_params'):
try:
return self.op.get_params(self)
return NoParams
except theano.gof.utils.MethodNotDefined:
return NoParams
def __getstate__(self):
d = self.__dict__
......
......@@ -795,6 +795,22 @@ class Op(utils.object2, PureOp, CLinkerOp):
Convenience class to bundle `PureOp` and `CLinkerOp`.
"""
# We add a default get_params() implementation which will try to detect params from the op
# if params_type is set to a ParamsType. If not, we raise a MethodNotDefined exception.
def get_params(self, node):
if hasattr(self, 'params_type') and isinstance(self.params_type, theano.gof.ParamsType):
wrapper = self.params_type
if not all(hasattr(self, field) for field in wrapper.fields):
raise AttributeError('%s: missing attributes for ParamsType parameter.' % type(self).__name__)
wrap_dict = dict()
for i in range(wrapper.length):
field = wrapper.fields[i]
_type = wrapper.types[i]
wrap_dict[field] = _type.filter(getattr(self, field), strict=False, allow_downcast=True)
return theano.gof.Params(wrapper, **wrap_dict)
raise theano.gof.utils.MethodNotDefined('get_params')
def prepare_node(self, node, storage_map, compute_map, impl):
"""
Make any special modifications that the Op needs before doing
......@@ -1377,7 +1393,25 @@ class COp(Op):
The names must be strings that are not a C keyword and the
values must be strings of literal C representations.
If op uses a :class:`theano.gof.params_type.ParamsType` as ``params_type``,
it returns:
- a default macro ``PARAMS_TYPE`` which defines the class name of the
corresponding C struct.
- a macro ``DTYPE_PARAM_key`` for every ``key`` in the ParamsType for which associated
type implements the method :func:`theano.gof.type.CLinkerType.c_element_type`.
``DTYPE_PARAM_key`` defines the primitive C type name of an item in a variable
associated to ``key``.
"""
if hasattr(self, 'params_type') and isinstance(self.params_type, theano.gof.ParamsType):
wrapper = self.params_type
params = [('PARAMS_TYPE', wrapper.name)]
for i in range(wrapper.length):
try:
params.append(('DTYPE_PARAM_' + wrapper.fields[i], wrapper.types[i].c_element_type()))
except utils.MethodNotDefined:
pass
return params
return []
def c_code_cache_version(self):
......
差异被折叠。
差异被折叠。
#section support_code_apply
int APPLY_SPECIFIC(quadratic_function)(PyArrayObject* tensor, DTYPE_INPUT_0 a, DTYPE_INPUT_0 b, DTYPE_INPUT_0 c) {
NpyIter* iterator = NpyIter_New(tensor,
NPY_ITER_READWRITE | NPY_ITER_EXTERNAL_LOOP | NPY_ITER_REFS_OK,
NPY_KEEPORDER, NPY_NO_CASTING, NULL);
if(iterator == NULL) {
PyErr_SetString(PyExc_RuntimeError, "Unable to iterate over a tensor for an elemwise operation.");
return -1;
}
NpyIter_IterNextFunc* get_next = NpyIter_GetIterNext(iterator, NULL);
char** data_ptr = NpyIter_GetDataPtrArray(iterator);
npy_intp* stride_ptr = NpyIter_GetInnerStrideArray(iterator);
npy_intp* innersize_ptr = NpyIter_GetInnerLoopSizePtr(iterator);
do {
char* data = *data_ptr;
npy_intp stride = *stride_ptr;
npy_intp count = *innersize_ptr;
while(count) {
DTYPE_INPUT_0 x = *((DTYPE_INPUT_0*)data);
*((DTYPE_INPUT_0*)data) = a*x*x + b*x + c;
data += stride;
--count;
}
} while(get_next(iterator));
NpyIter_Deallocate(iterator);
return 0;
}
int APPLY_SPECIFIC(compute_quadratic)(PyArrayObject* X, PyArrayObject** Y, PARAMS_TYPE* coeff) {
DTYPE_INPUT_0 a = (DTYPE_INPUT_0) (*(DTYPE_PARAM_a*) PyArray_GETPTR1(coeff->a, 0)); // 0-D TensorType.
DTYPE_INPUT_0 b = coeff->b; // Scalar.
DTYPE_INPUT_0 c = (DTYPE_INPUT_0) PyFloat_AsDouble(coeff->c); // Generic.
Py_XDECREF(*Y);
*Y = (PyArrayObject*)PyArray_EMPTY(PyArray_NDIM(X), PyArray_DIMS(X), TYPENUM_INPUT_0, PyArray_IS_F_CONTIGUOUS(X));
if (PyArray_CopyInto(*Y, X) != 0) {
PyErr_SetString(PyExc_RuntimeError, "Unable to copy input into output.");
return 1;
};
if (APPLY_SPECIFIC(quadratic_function)(*Y, a, b, c) != 0) {
PyErr_SetString(PyExc_RuntimeError, "Unable to compute quadratic function.");
return 1;
}
return 0;
}
......@@ -35,6 +35,19 @@ class CLinkerType(CLinkerObject):
"""
def c_element_type(self):
"""
Optional: Return the name of the primitive C type of items into variables
handled by this type.
e.g:
- For ``TensorType(dtype='int64', ...)``: should return ``"npy_int64"``.
- For ``GpuArrayType(dtype='int32', ...)``: should return ``"ga_int"``.
"""
raise MethodNotDefined("c_element_type", type(self), self.__class__.__name__)
def c_is_simple(self):
"""
Optional: Return True for small or builtin C types.
......
......@@ -573,3 +573,20 @@ def hash_from_file(file_path):
with open(file_path, 'rb') as f:
file_content = f.read()
return hash_from_code(file_content)
# Set of C and C++ keywords as defined (at March 2nd, 2017) in the pages below:
# - http://fr.cppreference.com/w/c/keyword
# - http://fr.cppreference.com/w/cpp/keyword
# Added `NULL` and `_Pragma` keywords.
c_cpp_keywords = {'_Alignas', '_Alignof', '_Atomic', '_Bool', '_Complex', '_Generic', '_Imaginary', '_Noreturn',
'_Pragma', '_Static_assert', '_Thread_local', 'alignas', 'alignof', 'and', 'and_eq', 'asm', 'auto',
'bitand', 'bitor', 'bool', 'break', 'case', 'catch', 'char', 'char16_t', 'char32_t', 'class', 'compl',
'const', 'const_cast', 'constexpr', 'continue', 'decltype', 'default', 'delete', 'do', 'double',
'dynamic_cast', 'else', 'enum', 'explicit', 'export', 'extern', 'false', 'float', 'for', 'friend',
'goto', 'if', 'inline', 'int', 'long', 'mutable', 'namespace', 'new', 'noexcept', 'not', 'not_eq',
'NULL', 'nullptr', 'operator', 'or', 'or_eq', 'private', 'protected', 'public', 'register',
'reinterpret_cast', 'restrict', 'return', 'short', 'signed', 'sizeof', 'static', 'static_assert',
'static_cast', 'struct', 'switch', 'template', 'this', 'thread_local', 'throw', 'true', 'try',
'typedef', 'typeid', 'typename', 'union', 'unsigned', 'using', 'virtual', 'void', 'volatile',
'wchar_t', 'while', 'xor', 'xor_eq'}
......@@ -459,6 +459,9 @@ class GpuArrayType(Type):
else:
return np.dtype(self.dtype).itemsize
def c_element_type(self):
return pygpu.gpuarray.dtype_to_ctype(self.dtype)
def c_declare(self, name, sub, check_input=True):
return """
PyGpuArrayObject *%(name)s;
......
......@@ -349,6 +349,9 @@ class Scalar(Type):
return True
return abs(diff) <= (abs(a) * tolerance) + (abs(b) * tolerance)
def c_element_type(self):
return self.dtype_specs()[1]
def c_headers(self, c_compiler):
l = ['<math.h>']
# These includes are needed by Scalar and TensorType,
......
......@@ -374,6 +374,9 @@ class TensorType(Type):
def __repr__(self):
return str(self)
def c_element_type(self):
return self.dtype_specs()[1]
def c_declare(self, name, sub, check_input=True):
"""
Override `CLinkerType.c_declare`.
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论