提交 e9f194f5 authored 作者: Olivier Delalleau's avatar Olivier Delalleau

Merged

......@@ -38,11 +38,13 @@ There are less methods to define for an Op than for a Type:
*Default:* The default behavior is to do nothing.
.. function:: c_compile_args()
c_no_compile_args()
c_headers()
c_libraries()
c_support_code()
Allows you to specify headers, libraries, special g++ arguments or
Allows you to specify headers, libraries,
special g++ arguments to add/exclude or
helper functions/structs that the type needs. See :ref:`op`.
......
......@@ -75,11 +75,13 @@ the most important ones:
decrease the appropriate reference counts.
.. function:: c_compile_args()
c_no_compile_args()
c_headers()
c_libraries()
c_support_code()
Allows you to specify headers, libraries, special g++ arguments or
Allows you to specify headers, libraries,
special g++ arguments to add/exclude or
helper functions/structs that the type needs. See :ref:`type`.
......
......@@ -260,7 +260,7 @@ Glossary of terminology
* making :term:`Apply` instances, which mean "apply this TOI to some particular inputs" (via the ``make_node``),
* performing the calculation of outputs from given inputs (via the ``perform``),
* producing c code to perform calculation of outputs from inputs (via ``c_code, c_code_cleanup, c_support_code, c_headers, c_libraries, c_compile_args``)
* producing c code to perform calculation of outputs from inputs (via ``c_code, c_code_cleanup, c_support_code, c_headers, c_libraries, c_compile_args, c_no_compile_args``)
* [optionally] building gradient-calculating graphs (via ``grad``).
See :ref:`intro_to_ops`.
......
......@@ -135,12 +135,10 @@ Now, using ``Module``:
m = M.Module()
n = T.scalar('n')
m.c = T.scalar() # state variables
m.inc = M.Method(n, [], c = m.c + n) # m.c <= m.c + n
m.dec = M.Method(n, [], c = m.c - n) # k.c <= k.c - n
m.dec = M.Method(n, [], updates = {m.c: m.c - n})#alternative syntax
m.inc = M.Method(n, [], updates = {m.c: m.c + n}) # m.c <= m.c + n
m.dec = M.Method(n, [], updates = {m.c: m.c - n}) # k.c <= k.c - n
#m.dec = M.Method(n, [], updates = {c: m.c - n})#global c don't exist
#m.dec = M.Method(n, [], m.c = m.c - n) #python don't suppor this syntax
#m.plus10 don't update the state
#m.plus10 does not update the state
m.plus10 = M.Method([], m.c + 10) # m.c is always accessible since it is a member of this mlass
inst = m.make(c = 0) # here, we make an "instance" of the module with c initialized to 0
......@@ -192,8 +190,8 @@ Using Module:
m = M.Module()
n = T.scalar('n')
m.c = T.scalar() # state variables
m.inc = M.Method(n, [], c = m.c + n) # m.c <= m.c + n
m.dec = M.Method(n, [], c = m.c - n) # m.c <= m.c - n
m.inc = M.Method(n, [], updates = {m.c: m.c + n}) # m.c <= m.c + n
m.dec = M.Method(n, [], updates = {m.c: m.c - n}) # m.c <= m.c - n
return m
m = M.Module()
......
......@@ -36,7 +36,7 @@ The ``inputs`` argument to ``theano.function`` is a list, containing the ``Varia
.. class:: In
.. method:: __init__(variable, name=None, value=None, update=None, mutable=False)
.. method:: __init__(variable, name=None, value=None, update=None, mutable=False, strict=False, autoname=True, implicit=None)
``variable``: a Variable instance. This will be assigned a value
before running the function, not computed from its owner.
......@@ -46,8 +46,12 @@ The ``inputs`` argument to ``theano.function`` is a list, containing the ``Varia
can be set by ``kwarg``, and its value can be accessed by
``self.<name>``. The default value is ``None``.
``value``: literal or Container. This is the default value of
the Input. The default value of this parameter is ``None``
``value``: literal or ``Container``. The initial/default value for this
input. If update is`` None``, this input acts just like
an argument with a default value in Python. If update is not ``None``,
changes to this
value will "stick around", whether due to an update or a user's
explicit action.
``update``: Variable instance. This expression Variable will
replace ``value`` after each function call. The default value is
......@@ -57,11 +61,28 @@ The ``inputs`` argument to ``theano.function`` is a list, containing the ``Varia
compiled function to modify the Python object being used as the
default value. The default value is ``False``.
``strict``: Bool (default: ``False`` ). ``True`` means that the value
you pass for this input must have exactly the right type. Otherwise, it
may be cast automatically to the proper type.
``autoname``: Bool. If set to ``True``, if ``name`` is ``None`` and
the Variable has a name, it will be taken as the input's
name. If autoname is set to ``False``, the name is the exact
value passed as the name parameter (possibly ``None``).
``implicit``: Bool or ``None`` (default: ``None``)
``True``: This input is implicit in the sense that the user is not allowed
to provide a value for it. Requires ``value`` to be set.
``False``: The user can provide a value for this input. Be careful
when ``value`` is a container, because providing an input value will
overwrite the content of this container.
``None``: Automatically choose between ``True`` or ``False`` depending on the
situation. It will be set to ``False`` in all cases except if
``value`` is a container (so that there is less risk of accidentally
overwriting its content without being aware of it).
Value: initial and default values
---------------------------------
......@@ -136,6 +157,31 @@ Theano's Module system uses this mechanism to share storage between Methods.
The container being shared doesn't have to correspond to the same Variable in both functions,
but that's usually how this mechanism is used.
Note that when an input's ``value`` parameter is a shared container, this
input is considered as implicit by default. This means it cannot be set by the
user.
If ``implicit`` is manually set to ``False``, then it can be set by the user,
but then it will overwrite the container's content, so one should be careful
when allowing this.
This is illustrated in the following example.
>>> dec(1, 0) # Try to manually set an implicit input
<type 'exceptions.TypeError'>: Tried to provide value for implicit input: s
>>> dec = function([x, In(s, update=(s-x), value=inc.container[s], implicit=False)], [])
>>> inc[s] = 2
>>> print dec[s] # Containers are shared
2.0
>>> dec(1)
[]
>>> print inc[s] # Calling dec decreased the value in inc's container
1.0
>>> dec(1, 0) # Update inc[s] with 0 - 1 = -1
[]
>>> print inc[s]
-1.0
>>> print dec[s] # Still shared
-1.0
Input Argument Restrictions
---------------------------
......@@ -168,8 +214,8 @@ instance explicitly with the ``autoname`` flag set to False.
Access to function values and containers
----------------------------------------
For each input, ``theano.function`` will create a ``Container`` if the
value was not already a ``Container``. At the time of a function call,
For each input, ``theano.function`` will create a ``Container`` if
``value`` was not already a ``Container`` (or if ``implicit`` was ``False``). At the time of a function call,
each of these containers must be filled with a value. Each input (but
especially ones with a default value or an update expression) may have a
value between calls. The function interface defines a way to get at
......
"""Define `SymbolicInput`, `SymbolicOutput`, `In`, `Out` """
__docformat__ = 'restructuredtext en'
from theano import gof
class SymbolicInput(object):
"""
Represents a symbolic input for use with function or FunctionMaker.
......@@ -27,9 +29,15 @@ class SymbolicInput(object):
autoname: Bool (default: True)
See the name option.
implicit: Bool (default: False)
See help(In). Note that 'None' is not allowed here, since we are in the
symbolic case.
"""
def __init__(self, variable, name=None, update=None, mutable=None, strict=False, autoname=True):
def __init__(self, variable, name=None, update=None, mutable=None, strict=False, autoname=True,
implicit=False):
assert implicit is not None # Safety check.
self.variable = variable
self.name = variable.name if (autoname and name is None) else name
if self.name is not None and not isinstance(self.name, str):
......@@ -37,6 +45,7 @@ class SymbolicInput(object):
self.update = update
self.mutable = mutable if (mutable is not None) else (update is not None)
self.strict = strict
self.implicit = implicit
def __str__(self):
if self.update:
......@@ -132,14 +141,39 @@ class In(SymbolicInput):
strict: Bool (default: False)
True: means that the value you pass for this input must have exactly the right type
False: the value you pass for this input may be casted automatically to the proper type
False: the value you pass for this input may be cast automatically to the proper type
autoname: Bool (default: True)
See the name option.
implicit: Bool or None (default: None)
True: This input is implicit in the sense that the user is not allowed
to provide a value for it. Requires 'value' to be set.
False: The user can provide a value for this input. Be careful when
'value' is a container, because providing an input value will
overwrite the content of this container.
None: Automatically choose between True or False depending on the
situation. It will be set to False in all cases except if 'value'
is a container (so that there is less risk of accidentally
overwriting its content without being aware of it).
"""
def __init__(self, variable, name=None, value=None, update=None, mutable=None, strict=False, autoname=True):
super(In, self).__init__(variable, name, update, mutable, strict, autoname)
# Note: the documentation above is duplicated in doc/topics/function.txt,
# try to keep it synchronized.
def __init__(self, variable, name=None, value=None, update=None,
mutable=None, strict=False, autoname=True,
implicit=None):
if implicit is None:
# TODO Having a default value being a Variable only makes sense
# if this is a SharedVariable. This should be changed once shared
# variables are part of Theano instead of living in a separate
# repository.
implicit = (isinstance(value, gof.Container) or
isinstance(value, gof.Variable))
super(In, self).__init__(variable, name, update, mutable, strict,
autoname, implicit = implicit)
self.value = value
if self.implicit and value is None:
raise TypeError('An implicit input must be given a default value')
class SymbolicOutput(object):
......
......@@ -37,6 +37,7 @@ predefined_linkers = {
'c&py' : gof.DualLinker(checker = check_equal)
}
#Keep default_linker the same as the one for default_mode
default_linker = 'c|py'
def register_linker(name, linker):
......@@ -63,7 +64,8 @@ predefined_optimizers = {
'fast_run_stable' : OPT_FAST_RUN_STABLE,
'fast_compile' : OPT_FAST_COMPILE
}
default_optimizer = 'merge'
#Keep default_optimizer the same as the one for default_mode
default_optimizer = 'fast_run'
def register_optimizer(name, opt):
"""Add a `Optimizer` which can be referred to by `name` in `Mode`."""
......@@ -157,6 +159,7 @@ predefined_modes = {'FAST_COMPILE': FAST_COMPILE,
# The default mode used by functions and modules is read from the environment
# variable THEANO_DEFAULT_MODE. Unit tests will run using this value. If the env. var.
# is not set, it will default to 'FAST_RUN'
# keep default_mode.optimizer==default_optimizer and default_mode.linker==default_linker!
##
default_mode = os.getenv('THEANO_DEFAULT_MODE','FAST_RUN')
......
......@@ -354,7 +354,7 @@ class Method(Component):
return memo[self]
self.resolve_all() # resolve all so we don't have to mess with strings
def get_storage(r, require = False):
def get_storage(r, require=False):
# If require is True, we can only get storage from the memo.
try:
return memo[r]
......@@ -405,7 +405,8 @@ class Method(Component):
variable=k,
update=v,
value=get_storage(k, not allocate_all).value,
mutable=True)
mutable=True,
implicit = True)
inputs.append(input_k)
else:
raise ValueError(('Variable listed in both inputs and updates.'
......@@ -437,6 +438,13 @@ class Method(Component):
assert storage.mutable == False
else:
storage = get_storage(input, not allocate_all)
# Declare as an implicit input.
# TODO Note from OD: is this dangerous? (in case this storage
# is shared, and would sometimes need to be implicit, sometimes
# not).
storage.implicit = True
assert type(storage) is io.In
inputs.append(storage)
......
......@@ -2,10 +2,11 @@ import time
from ..gof.link import WrapLinkerMany
from ..gof.cutils import run_cthunk
from ..compile.mode import Mode
from ..compile.mode import Mode, predefined_linkers
from ..gof.cc import OpWiseCLinker
class ProfileMode(Mode):
def __init__(self, linker, optimizer=None):
def __init__(self, linker=OpWiseCLinker(), optimizer=None):
local_time = [0.0]
apply_time = {}
op_time = {}
......@@ -31,6 +32,9 @@ class ProfileMode(Mode):
self.op_time = op_time
self.op_cimpl = op_cimpl
if isinstance(linker, str):
linker = predefined_linkers[linker]
wrap_linker = WrapLinkerMany([linker], [blah])
if optimizer:
super(ProfileMode, self).__init__(wrap_linker, optimizer)
......
......@@ -7,7 +7,7 @@ from theano.compile.function_module import *
from theano import tensor
from theano import tensor as T
import random
import random, theano
import numpy as N
......@@ -250,9 +250,30 @@ class T_function(unittest.TestCase):
self.failUnless(f[s] == 2)
self.failUnless(g[s] == 2)
f(1, 2)
g(1, 2)
self.failUnless(f[s] == 4)
self.failUnless(g[s] == 4)
g(1, 2) # has no effect on state
self.failUnless(f[s] == 4)
self.failUnless(g[s] == 4)
def test_shared_state_not_implicit(self):
# This test is taken from the documentation in
# doc/topics/function.txt. If it does not pass anymore and yet the
# behavior is still intended the doc and the test should both be
# updated accordingly.
x, s = T.scalars('xs')
inc = function([x, In(s, update=(s+x), value=10.0)], [])
dec = function([x, In(s, update=(s-x), value=inc.container[s],
implicit = False)], [])
self.failUnless(dec[s] is inc[s])
inc[s] = 2
self.failUnless(dec[s] == 2)
dec(1)
self.failUnless(inc[s] == 1)
dec(1, 0)
self.failUnless(inc[s] == -1)
self.failUnless(dec[s] == -1)
class T_picklefunction(unittest.TestCase):
......@@ -278,6 +299,13 @@ class T_picklefunction(unittest.TestCase):
self.failIf(g.container[2].storage is f.container[2].storage)
self.failIf(x in g.container)
self.failIf(x in g.value)
self.failUnless(len(f.defaults) == len(g.defaults))
print 'f.defaults = %s' % (f.defaults, )
print 'g.defaults = %s' % (g.defaults, )
self.failUnless(all([f_req == g_req and f_feed == g_feed and
f_val == g_val
for ((f_req, f_feed, f_val), (g_req, g_feed, g_val)) in zip(
f.defaults, g.defaults)]))
self.failIf(g.value[1] is f.value[1]) # should not have been copied
self.failIf(g.value[2] is f.value[2]) # should have been copied because it is mutable.
......@@ -287,6 +315,32 @@ class T_picklefunction(unittest.TestCase):
self.failUnless(f(2, 1) == g(2)) #they should be in sync, default value should be copied.
f(1,2) # put them out of sync
self.failIf(f(1, 2) == g(1, 2)) #they should not be equal anymore.
g(1, 2) # put them back in sync
self.failUnless(f(3) == g(3)) # They should be in sync again.
def test_deepcopy_shared_container(self):
# Ensure that shared containers remain shared after a deep copy.
a, x = T.scalars('ax')
h = function([In(a, value = 0.0)], a)
f = function([x, In(a, value=h.container[a], implicit = True)], x + a)
try:
memo = {}
ac = copy.deepcopy(a)
memo.update({id(a): ac})
hc = copy.deepcopy(h, memo = memo)
memo.update({id(h): hc})
fc = copy.deepcopy(f, memo = memo)
except NotImplementedError, e:
if e[0].startswith('DebugMode is not picklable'):
return
else:
raise
h[a] = 1
hc[ac] = 2
self.failUnless(f[a] == 1)
self.failUnless(fc[ac] == 2)
def test_pickle(self):
a = T.scalar() # the a is for 'anonymous' (un-named).
......@@ -472,7 +526,7 @@ if __name__ == '__main__':
if 1:
unittest.main()
else:
elif 0:
testcases = []
testcases.append(T_function)
......@@ -483,3 +537,11 @@ if __name__ == '__main__':
suite.addTest(testloader.loadTestsFromTestCase(testcase))
unittest.TextTestRunner(verbosity=2).run(suite)
#</boilerplate>
elif 0:
theano.compile.mode.default_mode = 'FAST_COMPILE'
t = T_picklefunction()
def fu(b):
assert b
t.failUnless = fu
t.test_deepcopy_shared_container()
......@@ -678,6 +678,23 @@ def test_method_mode():
assert m.h.maker.mode == m.g.maker.mode
assert numpy.all(m.f([1,2]) == m.g([1,2]))
def test_method_implicit_ticket_384():
"""
Ensure it is not possible to accidentally overwrite module variables
added as implicit inputs.
"""
M = Module()
M.x = T.scalar()
M.f = Method([M.x], M.x * 3)
m = M.make()
m.f(0)
try:
m.f(0, 0)
assert False
except TypeError, e:
if not str(e).startswith('Tried to provide value for implicit input'):
raise
def test_pickle():
"""Test that a module can be pickled"""
M = Module()
......
......@@ -526,10 +526,23 @@ class CLinker(link.Linker):
This might contain duplicates.
"""
ret = []
ret = ["-O3", "-w"]#-w means supress all warnings
# this is the param the -ffast-math activate. I put the explicitly as FillMissing must disable "-ffinite-math-only". Putting -ffast-math would make it disable all other parameter at the same time.
ret += ["-fno-math-errno", "-funsafe-math-optimizations",
"-fno-signaling-nans", "-fcx-limited-range",
"-fno-rounding-math", "-ffinite-math-only"]
for x in [y.type for y in self.variables] + [y.op for y in self.node_order]:
try: ret += x.c_compile_args()
except utils.MethodNotDefined: pass
ret=list(set(ret))#to remove duplicate
for x in [y.type for y in self.variables] + [y.op for y in self.node_order]:
try:
for i in x.c_no_compile_args():
try:
ret.remove(i)
except ValueError:
pass# in case the value is not there
except utils.MethodNotDefined: pass
return ret
def headers(self):
......@@ -703,16 +716,7 @@ class CLinker(link.Linker):
instantiate.customize.add_support_code(support_code)
instantiate.customize.add_support_code(self.struct_code)
instantiate.customize.add_support_code(static)
for extra_arg in (
"-O2",
"-ffast-math",
#"-fprefetch-loop-arrays",
#"-ftree-vect-loop-version",
#"-ftree-loop-optimize",
#"-ftree-vectorize"):
"-w" #-w means supress all warnings
):
instantiate.customize.add_extra_compile_arg(extra_arg)
for arg in self.compile_args():
instantiate.customize.add_extra_compile_arg(arg)
for header in self.headers():
......
......@@ -97,6 +97,25 @@ class CLinkerOp(object):
raise utils.MethodNotDefined('%s.c_compile_args' \
% self.__class__.__name__)
def c_no_compile_args(self):
"""Optional: Return a list of incompatible gcc compiler arguments.
We will remove those arguments from the command line of gcc. So if
another Op adds a compile arg in the graph that is incompatible
with this Op, the incompatible arg will not be used.
Useful for instance to remove -ffast-math.
EXAMPLE
WRITEME
:Exceptions:
- `MethodNotDefined`: the subclass does not override this method
"""
raise utils.MethodNotDefined('%s.c_no_compile_args' \
% self.__class__.__name__)
def c_headers(self):
"""Optional: Return a list of header files that must be included to compile the C code.
......
......@@ -148,6 +148,24 @@ class CLinkerType(object):
"""
raise MethodNotDefined("c_compile_args", type(self), self.__class__.__name__)
def c_no_compile_args(self):
"""Optional: Return a list of incompatible gcc compiler arguments.
We will remove those arguments from the command line of gcc. So if
another Op adds a compile arg in the graph that is incompatible
with this Op, the incompatible arg will not be used.
Useful for instance to remove -ffast-math.
EXAMPLE
WRITEME
:Exceptions:
- `MethodNotDefined`: the subclass does not override this method
"""
raise MethodNotDefined("c_no_compile_args", type(self), self.__class__.__name__)
def c_headers(self):
"""Optional: Return a list of header files required by code returned by
this class.
......
差异被折叠。
......@@ -2054,8 +2054,10 @@ class Reshape(Op):
The number of dimensions to which to reshape to (ndim) must be known at graph
build time."""
view_map = {0: [0]} #output 0 is potentially aliased to inputs [0]
def __init__(self, ndim):
def __init__(self, ndim, name = None):
self.ndim = ndim
if name:
self.name = name
def __eq__(self, other):
return (type(other) is Reshape) and (other.ndim == self.ndim)
def __hash__(self):
......@@ -2075,10 +2077,10 @@ class Reshape(Op):
def grad(self, (x, shp), (g_out,)):
return [reshape(g_out, shape(x), ndim=x.ndim), None]
def reshape(x, newshape, ndim=None):
def reshape(x, newshape, ndim=None, name=None):
if ndim is None:
ndim = get_vector_length(newshape)
op = Reshape(ndim)
op = Reshape(ndim, name)
return op(x, newshape)
......
......@@ -581,6 +581,11 @@ class CrossentropySoftmaxArgmax1HotWithBias(gof.Op):
""",
inside_row_loop,
"""
if ((y_i >= %(x)s->dimensions[1]) || (y_i < 0))
{
PyErr_SetString(PyExc_ValueError, "y_i value out of bounds");
%(fail)s;
}
nll_i[0] = - x_i[y_i*Sx]
- b_i[y_i*Sb]
+ row_max
......
......@@ -686,7 +686,7 @@ class Canonizer(gof.LocalOptimizer):
op = node.op
if op not in [self.main, self.inverse, self.reciprocal]:
return False
inputs = node.inputs
out = node.outputs[0]
assert len(node.outputs) == 1
......@@ -725,8 +725,14 @@ class Canonizer(gof.LocalOptimizer):
return getattr(self, 'name', 'Canonizer(%s, %s, %s)' % (self.main, self.inverse, self.reciprocal))
def mul_calculate(num, denum, aslist = False):
v = reduce(N.multiply, num, 1.0) / reduce(N.multiply, denum, 1.0)
def mul_calculate(num, denum, aslist=False):
if not num and not denum:
# Smallest 1 possible.
return [] if aslist else N.int8(1)
# Make sure we do not accidently upcast data types.
first = num[0] if num else denum[0]
one = N.asarray(first).dtype.type(1)
v = reduce(N.multiply, num, one) / reduce(N.multiply, denum, one)
if aslist:
if N.all(v == 1):
return []
......
......@@ -551,7 +551,8 @@ def test_naacl_model(iters_per_unsup=10, iters_per_sup=10,
s0 = str(m.finetuning_update(*(inputs + [targets])))
print iters_per_sup * (i+1), s0
if iters_per_sup == 10:
assert s0.startswith('15.6511')#should check for the 8 decimal only.
s0f = float(s0)
assert 15.6510 < s0f and s0f < 15.6512
def jtest_main():
from theano import gof
......
......@@ -57,9 +57,13 @@ class test_dimshuffle_lift(unittest.TestCase):
x, y, z = inputs([False]*1, [False]*2, [False]*3)
e = x + y + z
g = Env([x, y, z], [e])
self.failUnless(str(g) == "[add(InplaceDimShuffle{x,0,1}(add(InplaceDimShuffle{x,0}(x), y)), z)]", str(g))
self.failUnless(str(g) == ("[Elemwise{add,no_inplace}("
"InplaceDimShuffle{x,0,1}(Elemwise{add,no_inplace}"
"(InplaceDimShuffle{x,0}(x), y)), z)]"), str(g))
dimshuffle_lift.optimize(g)
self.failUnless(str(g) == "[add(add(InplaceDimShuffle{x,x,0}(x), InplaceDimShuffle{x,0,1}(y)), z)]", str(g))
self.failUnless(str(g) == ("[Elemwise{add,no_inplace}(Elemwise"
"{add,no_inplace}(InplaceDimShuffle{x,x,0}(x), InplaceDimShuffle"
"{x,0,1}(y)), z)]"), str(g))
def test_add_canonizer_problem0():
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论