提交 117f23f5 authored 作者: Michael Osthege's avatar Michael Osthege 提交者: Thomas Wiecki

Merge PureOp into Op, move C-related interface types

The CLinkerOp and CLinkerObject like types were moved into link.c.interface to get them out of gof. PureOp and Op were merged (as Op) which means that some C-related methods are now declared on all Ops.
上级 c236db89
......@@ -10,7 +10,7 @@ import theano.tensor as tt
from tests import unittest_tools as utt
from theano import Mode, function, tensor
from theano.gof import Apply, generic
from theano.gof.op import PureOp
from theano.gof.op import Op
from theano.ifelse import IfElse, ifelse
......@@ -509,7 +509,7 @@ class TestIfelse(utt.OptimizationTestMixin):
[(param, param - 0.5 * tensor.grad(cost=loss, wrt=param)) for param in params]
class IfElseIfElseIf(PureOp):
class IfElseIfElseIf(Op):
def __init__(self, inplace=False):
# check destroyhandler and others to ensure that a view_map with
self.inplace = inplace
......@@ -591,7 +591,7 @@ class NotImplementedOpException(Exception):
pass
class NotImplementedOp(PureOp):
class NotImplementedOp(Op):
def make_node(self, x):
return Apply(self, [x], [x.type()])
......
"""
Provides `DebugMode`, an evaluation mode for debugging theano internals.
TODO: add support for IfElse Op, LazyLinker, PureOp, etc.
TODO: add support for IfElse Op, LazyLinker, etc.
"""
......@@ -1823,12 +1823,6 @@ class _Linker(LocalLinker):
try:
if not self.maker.mode.check_c_code or debug:
raise utils.MethodNotDefined()
# Ops that do not inherit from gof.op.Op don't have certain
# methods defined that the CLinker expects (Scan is an
# example, ifelse is another of such classes that inherit
# directly from PureOp)
if not isinstance(node.op, gof.op.Op):
raise utils.MethodNotDefined()
node.op.prepare_node(node, storage_map, compute_map, "c")
thunk = node.op.make_c_thunk(
......@@ -1843,7 +1837,7 @@ class _Linker(LocalLinker):
# consider that we don't have a python implementation
if (
(self.maker.mode.check_py_code or thunks_c[-1] is None)
and node.op.perform.__code__ != gof.op.PureOp.perform.__code__
and node.op.perform.__code__ != gof.op.Op.perform.__code__
) or debug:
node.op.prepare_node(node, storage_map, compute_map, "py")
thunk = node.op.make_py_thunk(
......
......@@ -4,14 +4,7 @@ import theano
from theano.gof.destroyhandler import DestroyHandler
from theano.gof.fg import FunctionGraph, InconsistencyError, MissingInputError
from theano.gof.graph import Apply, Constant, Variable, view_roots
from theano.gof.op import (
COp,
Op,
OpenMPOp,
PureOp,
get_test_value,
ops_with_inner_function,
)
from theano.gof.op import COp, Op, OpenMPOp, get_test_value, ops_with_inner_function
from theano.gof.opt import (
CheckStackTraceOptimization,
EquilibriumOptimizer,
......
......@@ -65,8 +65,8 @@ class Apply(Node):
Basically, an `Apply` instance is an object that represents the
Python statement `outputs = op(*inputs)`.
This class is typically instantiated by a `PureOp.make_node` method, which
is called by `PureOp.__call__`.
This class is typically instantiated by a `Op.make_node` method, which
is called by `Op.__call__`.
The function `theano.compile.function.function` uses `Apply.inputs`
together with `Variable.owner` to search the expression graph and determine
......@@ -77,7 +77,7 @@ class Apply(Node):
Parameters
----------
op : A PureOp instance
op : A Op instance
inputs : list of Variable instances
outputs : list of Variable instances
......
差异被折叠。
......@@ -1231,7 +1231,7 @@ def local_optimizer(tracks, inplace=False, requirements=()):
f.__name__,
)
for t in tracks:
if not (isinstance(t, op.Op) or issubclass(t, op.PureOp)):
if not (isinstance(t, op.Op) or issubclass(t, op.Op)):
raise ValueError(
"Tracks are op classes or instances", f.__module__, f.__name__
)
......
......@@ -17,259 +17,14 @@ from theano.gof import graph, utils
########
# Type #
########
from theano.gof.op import CLinkerObject, Op
from theano.gof.op import Op
from theano.gof.utils import MethodNotDefined, object2
from theano.link.c.interface import CLinkerType
__docformat__ = "restructuredtext en"
class CLinkerType(CLinkerObject):
"""
Interface specification for Types that can be arguments to a `CLinkerOp`.
A CLinkerType instance is mainly responsible for providing the C code that
interfaces python objects with a C `CLinkerOp` implementation.
See WRITEME for a general overview of code generation by `CLinker`.
"""
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.
A hint to tell the compiler that this type is a builtin C type or a
small struct and that its memory footprint is negligible. Simple
objects may be passed on the stack.
"""
return False
def c_literal(self, data):
"""
Optional: WRITEME
Parameters
----------
data : WRITEME
WRITEME
Raises
------
MethodNotDefined
Subclass does not implement this method.
"""
raise MethodNotDefined("c_literal", type(self), self.__class__.__name__)
def c_declare(self, name, sub, check_input=True):
"""
Required: Return c code to declare variables that will be
instantiated by `c_extract`.
Parameters
----------
name: str
The name of the ``PyObject *`` pointer that will
the value for this Type
sub: dict string -> string
a dictionary of special codes. Most importantly
sub['fail']. See CLinker for more info on `sub` and ``fail``.
Notes
-----
It is important to include the `name` inside of variables which
are declared here, so that name collisions do not occur in the
source file that is generated.
The variable called ``name`` is not necessarily defined yet
where this code is inserted. This code might be inserted to
create class variables for example, whereas the variable ``name``
might only exist inside certain functions in that class.
TODO: Why should variable declaration fail? Is it even allowed to?
Raises
------
MethodNotDefined
Subclass does not implement this method.
Examples
--------
.. code-block: python
return "PyObject ** addr_of_%(name)s;"
"""
raise MethodNotDefined()
def c_init(self, name, sub):
"""
Required: Return c code to initialize the variables that were declared
by self.c_declare().
Notes
-----
The variable called ``name`` is not necessarily defined yet
where this code is inserted. This code might be inserted in a
class constructor for example, whereas the variable ``name``
might only exist inside certain functions in that class.
TODO: Why should variable initialization fail? Is it even allowed to?
Examples
--------
.. code-block: python
return "addr_of_%(name)s = NULL;"
"""
raise MethodNotDefined("c_init", type(self), self.__class__.__name__)
def c_extract(self, name, sub, check_input=True):
"""
Required: Return c code to extract a PyObject * instance.
The code returned from this function must be templated using
``%(name)s``, representing the name that the caller wants to
call this `Variable`. The Python object self.data is in a
variable called "py_%(name)s" and this code must set the
variables declared by c_declare to something representative
of py_%(name)s. If the data is improper, set an appropriate
exception and insert "%(fail)s".
TODO: Point out that template filling (via sub) is now performed
by this function. --jpt
Parameters
----------
name : str
The name of the ``PyObject *`` pointer that will
store the value for this Type.
sub : dict string -> string
A dictionary of special codes. Most importantly
sub['fail']. See CLinker for more info on `sub` and ``fail``.
Raises
------
MethodNotDefined
Subclass does not implement this method.
Examples
--------
.. code-block: python
return "if (py_%(name)s == Py_None)" + \\\
addr_of_%(name)s = &py_%(name)s;" + \\\
"else" + \\\
{ PyErr_SetString(PyExc_ValueError, \\\
'was expecting None'); %(fail)s;}"
"""
raise MethodNotDefined("c_extract", type(self), self.__class__.__name__)
def c_extract_out(self, name, sub, check_input=True):
"""
Optional: C code to extract a PyObject * instance.
Unlike c_extract, c_extract_out has to accept Py_None,
meaning that the variable should be left uninitialized.
"""
return """
if (py_%(name)s == Py_None)
{
%(c_init_code)s
}
else
{
%(c_extract_code)s
}
""" % dict(
name=name,
c_init_code=self.c_init(name, sub),
c_extract_code=self.c_extract(name, sub, check_input),
)
def c_cleanup(self, name, sub):
"""
Return C code to clean up after `c_extract`.
This returns C code that should deallocate whatever `c_extract`
allocated or decrease the reference counts. Do not decrease
py_%(name)s's reference count.
WRITEME
Parameters
----------
name : WRITEME
WRITEME
sub : WRITEME
WRITEME
Raises
------
MethodNotDefined
Subclass does not implement this method.
"""
raise MethodNotDefined()
def c_sync(self, name, sub):
"""
Required: Return C code to pack C types back into a PyObject.
The code returned from this function must be templated using
"%(name)s", representing the name that the caller wants to
call this Variable. The returned code may set "py_%(name)s"
to a PyObject* and that PyObject* will be accessible from
Python via variable.data. Do not forget to adjust reference
counts if "py_%(name)s" is changed from its original value.
Parameters
----------
name : WRITEME
WRITEME
sub : WRITEME
WRITEME
Raises
------
MethodNotDefined
Subclass does not implement this method.
"""
raise MethodNotDefined("c_sync", type(self), self.__class__.__name__)
def c_code_cache_version(self):
"""
Return a tuple of integers indicating the version of this Type.
An empty tuple indicates an 'unversioned' Type that will not
be cached between processes.
The cache mechanism may erase cached modules that have been
superceded by newer versions. See `ModuleCache` for details.
"""
return ()
class PureType:
"""
Interface specification for variable type instances.
......
差异被折叠。
......@@ -59,7 +59,7 @@ from theano.compile.function import function
from theano.compile.io import In, Out
from theano.compile.mode import AddFeatureOptimizer
from theano.compile.profiling import ScanProfileStats
from theano.gof import Apply, PureOp
from theano.gof import Apply, Op
from theano.gof.graph import equal_computations, io_connection_pattern
from theano.gof.toolbox import NoOutputFromInplace
from theano.gradient import DisconnectedType, NullType, grad_undefined
......@@ -80,7 +80,7 @@ __contact__ = "Razvan Pascanu <r.pascanu@gmail>"
_logger = logging.getLogger("theano.scan.op")
class Scan(PureOp):
class Scan(Op):
"""
Parameters
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论