-
Notifications
You must be signed in to change notification settings - Fork 2.5k
/
sharedvalue.py
300 lines (242 loc) · 10.3 KB
/
sharedvalue.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
"""
Provide a simple user friendly API to Theano-managed memory.
"""
# Standard imports
from __future__ import absolute_import, print_function, division
import copy
import logging
# Third-party imports
import numpy as np
# Theano imports
from theano.gof import Container, Variable, generic, utils
_logger = logging.getLogger('theano.compile.sharedvalue')
__docformat__ = 'restructuredtext en'
class SharedVariable(Variable):
"""
Variable that is (defaults to being) shared between functions that
it appears in.
Parameters
----------
name : str
The name for this variable (see `Variable`).
type : str
The type for this variable (see `Variable`).
value
A value to associate with this variable (a new container will be
created).
strict
True : assignments to .value will not be cast or copied, so they must
have the correct type.
allow_downcast
Only applies if `strict` is False.
True : allow assigned value to lose precision when cast during
assignment.
False : never allow precision loss.
None : only allow downcasting of a Python float to a scalar floatX.
container
The container to use for this variable. Illegal to pass this as well as
a value.
Notes
-----
For more user-friendly constructor, see `shared`.
"""
# Container object
container = None
"""
A container to use for this SharedVariable when it is an implicit
function parameter.
:type: `Container`
"""
# default_update
# If this member is present, its value will be used as the "update" for
# this Variable, unless another update value has been passed to "function",
# or the "no_default_updates" list passed to "function" contains it.
def __init__(self, name, type, value, strict,
allow_downcast=None, container=None):
super(SharedVariable, self).__init__(type=type, name=name,
owner=None, index=None)
if container is not None:
self.container = container
if (value is not None) or (strict is not None):
raise TypeError('value and strict are ignored if you pass '
'a container here')
else:
self.container = Container(
self,
storage=[type.filter(value, strict=strict,
allow_downcast=allow_downcast)],
readonly=False,
strict=strict,
allow_downcast=allow_downcast)
def get_value(self, borrow=False, return_internal_type=False):
"""
Get the non-symbolic value associated with this SharedVariable.
Parameters
----------
borrow : bool
True to permit returning of an object aliased to internal memory.
return_internal_type : bool
True to permit the returning of an arbitrary type object used
internally to store the shared variable.
Only with borrow=False and return_internal_type=True does this function
guarantee that you actually get the internal object.
But in that case, you may get different return types when using
different compute devices.
"""
if borrow:
return self.container.value
else:
return copy.deepcopy(self.container.value)
def set_value(self, new_value, borrow=False):
"""
Set the non-symbolic value associated with this SharedVariable.
Parameters
----------
borrow : bool
True to use the new_value directly, potentially creating problems
related to aliased memory.
Changes to this value will be visible to all functions using
this SharedVariable.
Notes
-----
Set_value will work in-place on the GPU, if
the following conditions are met:
* The destination on the GPU must be c_contiguous.
* The source is on the CPU.
* The old value must have the same dtype as the new value
(which is a given for now, since only float32 is
supported).
* The old and new value must have the same shape.
* The old value is being completely replaced by the new
value (not partially modified, e.g. by replacing some
subtensor of it).
It is also worth mentioning that, for efficient transfer to the GPU,
Theano will make the new data ``c_contiguous``. This can require an
extra copy of the data on the host.
The inplace on gpu memory work when borrow is either True or False.
"""
if borrow:
self.container.value = new_value
else:
self.container.value = copy.deepcopy(new_value)
def zero(self, borrow=False):
"""
Set the values of a shared variable to 0.
Parameters
----------
borrow : bbol
True to modify the value of a shared variable directly by using
its previous value. Potentially this can cause problems
regarding to the aliased memory.
Changes done with this function will be visible to all functions using
this SharedVariable.
"""
if borrow:
self.container.value[...] = 0
else:
self.container.value = 0 * self.container.value
def clone(self):
cp = self.__class__(
name=self.name,
type=self.type,
value=None,
strict=None,
container=self.container)
cp.tag = copy.copy(self.tag)
return cp
def __getitem__(self, *args):
# __getitem__ is not available for generic SharedVariable objects.
# We raise a TypeError like Python would do if __getitem__ was not
# implemented at all, but with a more explicit error message to help
# Theano users figure out the root of the problem more easily.
value = self.get_value(borrow=True)
if isinstance(value, np.ndarray):
# Array probably had an unknown dtype.
msg = ("a Numpy array with dtype: '%s'. This data type is not "
"currently recognized by Theano tensors: please cast "
"your data into a supported numeric type if you need "
"Theano tensor functionalities." % value.dtype)
else:
msg = ('an object of type: %s. Did you forget to cast it into '
'a Numpy array before calling theano.shared()?' %
type(value))
raise TypeError(
"The generic 'SharedVariable' object is not subscriptable. "
"This shared variable contains %s" % msg)
def _value_get(self):
raise Exception("sharedvar.value does not exist anymore. Use "
"sharedvar.get_value() or sharedvar.set_value()"
" instead.")
def _value_set(self, new_value):
raise Exception("sharedvar.value does not exist anymore. Use "
"sharedvar.get_value() or sharedvar.set_value()"
" instead.")
# We keep this just to raise an error
value = property(_value_get, _value_set)
def shared_constructor(ctor, remove=False):
if remove:
shared.constructors.remove(ctor)
else:
shared.constructors.append(ctor)
return ctor
def shared(value, name=None, strict=False, allow_downcast=None, **kwargs):
"""Return a SharedVariable Variable, initialized with a copy or
reference of `value`.
This function iterates over constructor functions to find a
suitable SharedVariable subclass. The suitable one is the first
constructor that accept the given value. See the documentation of
:func:`shared_constructor` for the definition of a constructor
function.
This function is meant as a convenient default. If you want to use a
specific shared variable constructor, consider calling it directly.
``theano.shared`` is a shortcut to this function.
.. attribute:: constructors
A list of shared variable constructors that will be tried in reverse
order.
Notes
-----
By passing kwargs, you effectively limit the set of potential constructors
to those that can accept those kwargs.
Some shared variable have ``borrow`` as extra kwargs.
`See <http://deeplearning.net/software/theano/tutorial/aliasing.\
html#borrowing-when-creating-shared-variables>`_ for details.
Some shared variable have ``broadcastable`` as extra kwargs. As shared
variable shapes can change, all dimensions default to not being
broadcastable, even if ``value`` has a shape of 1 along some dimension.
This parameter allows you to create for example a `row` or `column` 2d
tensor.
"""
try:
if isinstance(value, Variable):
raise TypeError("Shared variable constructor needs numeric "
"values and not symbolic variables.")
for ctor in reversed(shared.constructors):
try:
var = ctor(value, name=name, strict=strict,
allow_downcast=allow_downcast, **kwargs)
utils.add_tag_trace(var)
return var
except TypeError:
continue
# This may happen when kwargs were supplied
# if kwargs were given, the generic_constructor won't be callable.
#
# This was done on purpose, the rationale being that if kwargs
# were supplied, the user didn't want them to be ignored.
except MemoryError as e:
e.args = e.args + ('you might consider'
' using \'theano.shared(..., borrow=True)\'',)
raise
raise TypeError('No suitable SharedVariable constructor could be found.'
' Are you sure all kwargs are supported?'
' We do not support the parameter dtype or type.'
' value="%s". parameters="%s"' %
(value, kwargs))
shared.constructors = []
@shared_constructor
def generic_constructor(value, name=None, strict=False, allow_downcast=None):
"""
SharedVariable Constructor.
"""
return SharedVariable(type=generic, value=value, name=name, strict=strict,
allow_downcast=allow_downcast)