/theano/compile/ops.py

https://github.com/yosinski/Theano
Python | 278 lines | 225 code | 27 blank | 26 comment | 12 complexity | 60667fcabcfd748d1ce663a3877fd5d1 MD5 | raw file
  1. """This file contain auxiliary Ops, used during the compilation phase."""
  2. import copy
  3. import warnings
  4. import theano
  5. from theano import gof
  6. def register_view_op_c_code(type, code, version=()):
  7. """ Tell ViewOp how to generate C code for a Theano Type
  8. :param typ: A Theano type. It must be the Theano class itself and not an
  9. instance of the class.
  10. :param code: C code that deep copies the Theano type 'typ'.
  11. Use %(iname)s and %(oname)s for the input and output C
  12. variable names respectively.
  13. :param version: A number indicating the version of the code, for cache.
  14. """
  15. ViewOp.c_code_and_version[type] = (code, version)
  16. class ViewOp(gof.Op):
  17. """
  18. Returns an inplace view of the input. Used internally by Theano.
  19. """
  20. view_map = {0: [0]}
  21. # Mapping from Type to C code (and version) to use.
  22. # In the C code, the name of the input variable is %(iname)s,
  23. # the output variable is %(oname)s.
  24. c_code_and_version = {}
  25. def make_node(self, x):
  26. return gof.Apply(self, [x], [x.type()])
  27. def __eq__(self, other):
  28. return type(self) == type(other)
  29. def __hash__(self):
  30. return hash(type(self))
  31. def perform(self, node, inp, out):
  32. x, = inp
  33. z, = out
  34. z[0] = x
  35. def __str__(self):
  36. return '%s' % self.__class__.__name__
  37. def c_code(self, node, nodename, inp, out, sub):
  38. iname, = inp
  39. oname, = out
  40. fail = sub['fail']
  41. itype = node.inputs[0].type.__class__
  42. if itype in self.c_code_and_version:
  43. code, version = self.c_code_and_version[itype]
  44. return code % locals()
  45. # Else, no C code
  46. return super(ViewOp, self).c_code(node, nodename, inp, out, sub)
  47. def c_code_cache_version(self):
  48. version = []
  49. # If any of the c code is unversionned, we have to return ()
  50. # Else, we will return a list of (type name, version) pairs.
  51. for t, (c, v) in sorted(self.c_code_and_version.items(), key=lambda pair: str(pair[0])):
  52. if not v:
  53. warnings.warn("Type %s has C code for ViewOp, but it has "
  54. "no version. You should add a 'version' keyword arg "
  55. "when calling register_deep_copy_op_c_code." % t,
  56. stacklevel=2)
  57. return ()
  58. version.append((str(t), v))
  59. return tuple(version)
  60. def infer_shape(self, node, input_shapes):
  61. return input_shapes
  62. def grad(self, args, g_outs):
  63. return g_outs
  64. view_op = ViewOp()
  65. class OutputGuard(ViewOp):
  66. """
  67. This op is used only internally by Theano.
  68. Only the AddDestroyHandler optimizer tries to insert them in the graph.
  69. This Op is declared as destructive while it is not destroying
  70. anything. It returns a view. This is used to prevent destruction of
  71. the output variables of a Theano function.
  72. There is a mechanism in Theano that should prevent this, but the use
  73. of OutputGuard adds a safeguard: it may be possible for some optimization
  74. run before the add_destroy_handler phase to bypass this mechanism, by
  75. making in-place optimizations.
  76. TODO: find a current full explanation.
  77. """
  78. destroy_map = {0: [0]}
  79. _output_guard = OutputGuard()
  80. def register_deep_copy_op_c_code(typ, code, version=()):
  81. """ Tell DeepCopyOp how to generate C code for a Theano Type
  82. :param typ: A Theano type. It must be the Theano class itself and not an
  83. instance of the class.
  84. :param code: C code that deep copies the Theano type 'typ'.
  85. Use %(iname)s and %(oname)s for the input and output C
  86. variable names respectively.
  87. :param version: A number indicating the version of the code, for cache.
  88. """
  89. DeepCopyOp.c_code_and_version[typ] = (code, version)
  90. class DeepCopyOp(gof.Op):
  91. # Mapping from Type to C code (and version) to use.
  92. # In the C code, the name of the input variable is %(iname)s,
  93. # the output variable is %(oname)s.
  94. c_code_and_version = {}
  95. def __init__(self):
  96. pass
  97. def __str__(self):
  98. return self.__class__.__name__
  99. def __hash__(self):
  100. return hash(type(self))
  101. def __eq__(self, other):
  102. return type(self) == type(other)
  103. def make_node(self, x):
  104. return gof.Apply(self, [x], [x.type()])
  105. def perform(self, node, args, outs):
  106. if hasattr(args[0], 'copy'):
  107. #when args[0] is a an ndarray of 0 dimensions,
  108. #this return a numpy.dtype and not an ndarray
  109. #So when the args have a copy attribute we use it
  110. #as this don't have this problem
  111. outs[0][0] = args[0].copy()
  112. else:
  113. outs[0][0] = copy.deepcopy(args[0])
  114. def c_code_cache_version(self):
  115. version = []
  116. # If any of the c code is unversionned, we have to return ()
  117. # Else, we will return a list of (type name, version) pairs.
  118. for t, (c, v) in sorted(self.c_code_and_version.items(), key=lambda pair: str(pair[0])):
  119. if not v:
  120. warnings.warn("Type %s has C code for DeepCopyOp, but it has "
  121. "no version. You should add a 'version' keyword arg "
  122. "when calling register_OutputGuard_c_code." % t,
  123. stacklevel=2)
  124. return ()
  125. version.append((str(t), v))
  126. return tuple(version)
  127. def c_code(self, node, name, inames, onames, sub):
  128. iname, = inames
  129. oname, = onames
  130. fail = sub['fail']
  131. itype = node.inputs[0].type.__class__
  132. if itype in self.c_code_and_version:
  133. code, version = self.c_code_and_version[itype]
  134. return code % locals()
  135. # Else, no C code
  136. return super(DeepCopyOp, self).c_code(node, name, inames, onames, sub)
  137. deep_copy_op = DeepCopyOp()
  138. class Shape_i(gof.Op):
  139. """
  140. L{Op} to return the shape of a matrix.
  141. @note: Non-differentiable.
  142. """
  143. # Mapping from Type to C code (and version) to use.
  144. # In the C code, the name of the input variable is %(iname)s,
  145. # the output variable is %(oname)s.
  146. c_code_and_version = {}
  147. def __init__(self, i):
  148. self.i = i
  149. def __hash__(self):
  150. return hash(type(self)) ^ self.i
  151. def __eq__(self, other):
  152. return type(self) == type(other) and self.i == other.i
  153. def __str__(self):
  154. return '%s{%i}' % (self.__class__.__name__, self.i)
  155. def make_node(self, x):
  156. # x could be one of a number of types
  157. # the only thing we require is that the variable have a .ndim,
  158. # and that the value have a .shape
  159. if not isinstance(x, theano.Variable):
  160. raise TypeError('x must be Variable with ndim attribute', x)
  161. if x.ndim <= self.i:
  162. raise TypeError('x has too few dimensions for Shape_i',
  163. (x, self.i))
  164. return theano.Apply(self, [x], [theano.tensor.lscalar()])
  165. def perform(self, node, inp, out_):
  166. x, = inp
  167. out, = out_
  168. if out[0] is None:
  169. out[0] = theano._asarray(x.shape[self.i], dtype='int64')
  170. else:
  171. out[0][...] = x.shape[self.i]
  172. def c_code_cache_version(self):
  173. version = []
  174. # If any of the c code is unversionned, we have to return ()
  175. # Else, we will return a list of (type name, version) pairs.
  176. for t, (c, v) in sorted(self.c_code_and_version.items(),
  177. key=lambda pair: str(pair[0])):
  178. if not v:
  179. warnings.warn("Type %s has C code for Shape_i, but it has "
  180. "no version. You should add a 'version' keyword arg "
  181. "when calling register_OutputGuard_c_code." % t,
  182. stacklevel=2)
  183. return ()
  184. version.append((str(t), v))
  185. return tuple(version)
  186. def c_code(self, node, name, inames, onames, sub):
  187. iname, = inames
  188. oname, = onames
  189. fail = sub['fail']
  190. i = self.i
  191. itype = node.inputs[0].type.__class__
  192. if itype in self.c_code_and_version:
  193. code, version = self.c_code_and_version[itype]
  194. return code % locals()
  195. # Else, no C code
  196. return super(Shape_i, self).c_code(node, name, inames, onames, sub)
  197. def infer_shape(self, node, input_shapes):
  198. return [()]
  199. def grad(self, inp, grads):
  200. return [None]
  201. def register_shape_i_c_code(typ, code, version=()):
  202. """ Tell DeepCopyOp how to generate C code for a Theano Type
  203. :param typ: A Theano type. It must be the Theano class itself and not an
  204. instance of the class.
  205. :param code: C code that deep copies the Theano type 'typ'.
  206. Use %(iname)s and %(oname)s for the input and output C
  207. variable names respectively.
  208. :param version: A number indicating the version of the code, for cache.
  209. """
  210. Shape_i.c_code_and_version[typ] = (code, version)
  211. # List of Theano Types that one can add an extra dimension and for which
  212. # Scan can deal with.
  213. expandable_types = ()