提交 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: ...@@ -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. *Default:* The default behavior is to do nothing.
.. function:: c_compile_args() .. function:: c_compile_args()
c_no_compile_args()
c_headers() c_headers()
c_libraries() c_libraries()
c_support_code() 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`. helper functions/structs that the type needs. See :ref:`op`.
......
...@@ -75,11 +75,13 @@ the most important ones: ...@@ -75,11 +75,13 @@ the most important ones:
decrease the appropriate reference counts. decrease the appropriate reference counts.
.. function:: c_compile_args() .. function:: c_compile_args()
c_no_compile_args()
c_headers() c_headers()
c_libraries() c_libraries()
c_support_code() 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`. helper functions/structs that the type needs. See :ref:`type`.
......
...@@ -260,7 +260,7 @@ Glossary of terminology ...@@ -260,7 +260,7 @@ Glossary of terminology
* making :term:`Apply` instances, which mean "apply this TOI to some particular inputs" (via the ``make_node``), * 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``), * 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``). * [optionally] building gradient-calculating graphs (via ``grad``).
See :ref:`intro_to_ops`. See :ref:`intro_to_ops`.
......
...@@ -135,12 +135,10 @@ Now, using ``Module``: ...@@ -135,12 +135,10 @@ Now, using ``Module``:
m = M.Module() m = M.Module()
n = T.scalar('n') n = T.scalar('n')
m.c = T.scalar() # state variables m.c = T.scalar() # state variables
m.inc = 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, [], c = m.c - n) # k.c <= k.c - n m.dec = M.Method(n, [], updates = {m.c: m.c - n}) # k.c <= k.c - n
m.dec = M.Method(n, [], updates = {m.c: m.c - n})#alternative syntax
#m.dec = M.Method(n, [], updates = {c: m.c - n})#global c don't exist #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 does not update the state
#m.plus10 don't update the state
m.plus10 = M.Method([], m.c + 10) # m.c is always accessible since it is a member of this mlass 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 inst = m.make(c = 0) # here, we make an "instance" of the module with c initialized to 0
...@@ -192,8 +190,8 @@ Using Module: ...@@ -192,8 +190,8 @@ Using Module:
m = M.Module() m = M.Module()
n = T.scalar('n') n = T.scalar('n')
m.c = T.scalar() # state variables m.c = T.scalar() # state variables
m.inc = 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, [], 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 return m
m = M.Module() m = M.Module()
......
...@@ -36,7 +36,7 @@ The ``inputs`` argument to ``theano.function`` is a list, containing the ``Varia ...@@ -36,7 +36,7 @@ The ``inputs`` argument to ``theano.function`` is a list, containing the ``Varia
.. class:: In .. 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 ``variable``: a Variable instance. This will be assigned a value
before running the function, not computed from its owner. 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 ...@@ -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 can be set by ``kwarg``, and its value can be accessed by
``self.<name>``. The default value is ``None``. ``self.<name>``. The default value is ``None``.
``value``: literal or Container. This is the default value of ``value``: literal or ``Container``. The initial/default value for this
the Input. The default value of this parameter is ``None`` 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 ``update``: Variable instance. This expression Variable will
replace ``value`` after each function call. The default value is 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 ...@@ -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 compiled function to modify the Python object being used as the
default value. The default value is ``False``. 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 ``autoname``: Bool. If set to ``True``, if ``name`` is ``None`` and
the Variable has a name, it will be taken as the input's 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 name. If autoname is set to ``False``, the name is the exact
value passed as the name parameter (possibly ``None``). 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 Value: initial and default values
--------------------------------- ---------------------------------
...@@ -136,6 +157,31 @@ Theano's Module system uses this mechanism to share storage between Methods. ...@@ -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, 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. 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 Input Argument Restrictions
--------------------------- ---------------------------
...@@ -168,8 +214,8 @@ instance explicitly with the ``autoname`` flag set to False. ...@@ -168,8 +214,8 @@ instance explicitly with the ``autoname`` flag set to False.
Access to function values and containers Access to function values and containers
---------------------------------------- ----------------------------------------
For each input, ``theano.function`` will create a ``Container`` if the For each input, ``theano.function`` will create a ``Container`` if
value was not already a ``Container``. At the time of a function call, ``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 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 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 value between calls. The function interface defines a way to get at
......
"""Define `SymbolicInput`, `SymbolicOutput`, `In`, `Out` """ """Define `SymbolicInput`, `SymbolicOutput`, `In`, `Out` """
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
from theano import gof
class SymbolicInput(object): class SymbolicInput(object):
""" """
Represents a symbolic input for use with function or FunctionMaker. Represents a symbolic input for use with function or FunctionMaker.
...@@ -27,9 +29,15 @@ class SymbolicInput(object): ...@@ -27,9 +29,15 @@ class SymbolicInput(object):
autoname: Bool (default: True) autoname: Bool (default: True)
See the name option. 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.variable = variable
self.name = variable.name if (autoname and name is None) else name self.name = variable.name if (autoname and name is None) else name
if self.name is not None and not isinstance(self.name, str): if self.name is not None and not isinstance(self.name, str):
...@@ -37,6 +45,7 @@ class SymbolicInput(object): ...@@ -37,6 +45,7 @@ class SymbolicInput(object):
self.update = update self.update = update
self.mutable = mutable if (mutable is not None) else (update is not None) self.mutable = mutable if (mutable is not None) else (update is not None)
self.strict = strict self.strict = strict
self.implicit = implicit
def __str__(self): def __str__(self):
if self.update: if self.update:
...@@ -132,14 +141,39 @@ class In(SymbolicInput): ...@@ -132,14 +141,39 @@ class In(SymbolicInput):
strict: Bool (default: False) strict: Bool (default: False)
True: means that the value you pass for this input must have exactly the right type 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) autoname: Bool (default: True)
See the name option. 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): # Note: the documentation above is duplicated in doc/topics/function.txt,
super(In, self).__init__(variable, name, update, mutable, strict, autoname) # 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 self.value = value
if self.implicit and value is None:
raise TypeError('An implicit input must be given a default value')
class SymbolicOutput(object): class SymbolicOutput(object):
......
...@@ -37,6 +37,7 @@ predefined_linkers = { ...@@ -37,6 +37,7 @@ predefined_linkers = {
'c&py' : gof.DualLinker(checker = check_equal) 'c&py' : gof.DualLinker(checker = check_equal)
} }
#Keep default_linker the same as the one for default_mode
default_linker = 'c|py' default_linker = 'c|py'
def register_linker(name, linker): def register_linker(name, linker):
...@@ -63,7 +64,8 @@ predefined_optimizers = { ...@@ -63,7 +64,8 @@ predefined_optimizers = {
'fast_run_stable' : OPT_FAST_RUN_STABLE, 'fast_run_stable' : OPT_FAST_RUN_STABLE,
'fast_compile' : OPT_FAST_COMPILE '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): def register_optimizer(name, opt):
"""Add a `Optimizer` which can be referred to by `name` in `Mode`.""" """Add a `Optimizer` which can be referred to by `name` in `Mode`."""
...@@ -157,6 +159,7 @@ predefined_modes = {'FAST_COMPILE': FAST_COMPILE, ...@@ -157,6 +159,7 @@ predefined_modes = {'FAST_COMPILE': FAST_COMPILE,
# The default mode used by functions and modules is read from the environment # 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. # variable THEANO_DEFAULT_MODE. Unit tests will run using this value. If the env. var.
# is not set, it will default to 'FAST_RUN' # 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') default_mode = os.getenv('THEANO_DEFAULT_MODE','FAST_RUN')
......
...@@ -354,7 +354,7 @@ class Method(Component): ...@@ -354,7 +354,7 @@ class Method(Component):
return memo[self] return memo[self]
self.resolve_all() # resolve all so we don't have to mess with strings 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. # If require is True, we can only get storage from the memo.
try: try:
return memo[r] return memo[r]
...@@ -405,7 +405,8 @@ class Method(Component): ...@@ -405,7 +405,8 @@ class Method(Component):
variable=k, variable=k,
update=v, update=v,
value=get_storage(k, not allocate_all).value, value=get_storage(k, not allocate_all).value,
mutable=True) mutable=True,
implicit = True)
inputs.append(input_k) inputs.append(input_k)
else: else:
raise ValueError(('Variable listed in both inputs and updates.' raise ValueError(('Variable listed in both inputs and updates.'
...@@ -437,6 +438,13 @@ class Method(Component): ...@@ -437,6 +438,13 @@ class Method(Component):
assert storage.mutable == False assert storage.mutable == False
else: else:
storage = get_storage(input, not allocate_all) 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 assert type(storage) is io.In
inputs.append(storage) inputs.append(storage)
......
...@@ -2,10 +2,11 @@ import time ...@@ -2,10 +2,11 @@ import time
from ..gof.link import WrapLinkerMany from ..gof.link import WrapLinkerMany
from ..gof.cutils import run_cthunk 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): class ProfileMode(Mode):
def __init__(self, linker, optimizer=None): def __init__(self, linker=OpWiseCLinker(), optimizer=None):
local_time = [0.0] local_time = [0.0]
apply_time = {} apply_time = {}
op_time = {} op_time = {}
...@@ -31,6 +32,9 @@ class ProfileMode(Mode): ...@@ -31,6 +32,9 @@ class ProfileMode(Mode):
self.op_time = op_time self.op_time = op_time
self.op_cimpl = op_cimpl self.op_cimpl = op_cimpl
if isinstance(linker, str):
linker = predefined_linkers[linker]
wrap_linker = WrapLinkerMany([linker], [blah]) wrap_linker = WrapLinkerMany([linker], [blah])
if optimizer: if optimizer:
super(ProfileMode, self).__init__(wrap_linker, optimizer) super(ProfileMode, self).__init__(wrap_linker, optimizer)
......
...@@ -7,7 +7,7 @@ from theano.compile.function_module import * ...@@ -7,7 +7,7 @@ from theano.compile.function_module import *
from theano import tensor from theano import tensor
from theano import tensor as T from theano import tensor as T
import random import random, theano
import numpy as N import numpy as N
...@@ -250,9 +250,30 @@ class T_function(unittest.TestCase): ...@@ -250,9 +250,30 @@ class T_function(unittest.TestCase):
self.failUnless(f[s] == 2) self.failUnless(f[s] == 2)
self.failUnless(g[s] == 2) self.failUnless(g[s] == 2)
f(1, 2) f(1, 2)
g(1, 2)
self.failUnless(f[s] == 4) self.failUnless(f[s] == 4)
self.failUnless(g[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): class T_picklefunction(unittest.TestCase):
...@@ -278,6 +299,13 @@ 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(g.container[2].storage is f.container[2].storage)
self.failIf(x in g.container) self.failIf(x in g.container)
self.failIf(x in g.value) 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[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. 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): ...@@ -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. 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 f(1,2) # put them out of sync
self.failIf(f(1, 2) == g(1, 2)) #they should not be equal anymore. 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): def test_pickle(self):
a = T.scalar() # the a is for 'anonymous' (un-named). a = T.scalar() # the a is for 'anonymous' (un-named).
...@@ -472,7 +526,7 @@ if __name__ == '__main__': ...@@ -472,7 +526,7 @@ if __name__ == '__main__':
if 1: if 1:
unittest.main() unittest.main()
else: elif 0:
testcases = [] testcases = []
testcases.append(T_function) testcases.append(T_function)
...@@ -483,3 +537,11 @@ if __name__ == '__main__': ...@@ -483,3 +537,11 @@ if __name__ == '__main__':
suite.addTest(testloader.loadTestsFromTestCase(testcase)) suite.addTest(testloader.loadTestsFromTestCase(testcase))
unittest.TextTestRunner(verbosity=2).run(suite) unittest.TextTestRunner(verbosity=2).run(suite)
#</boilerplate> #</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(): ...@@ -678,6 +678,23 @@ def test_method_mode():
assert m.h.maker.mode == m.g.maker.mode assert m.h.maker.mode == m.g.maker.mode
assert numpy.all(m.f([1,2]) == m.g([1,2])) 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(): def test_pickle():
"""Test that a module can be pickled""" """Test that a module can be pickled"""
M = Module() M = Module()
......
...@@ -526,10 +526,23 @@ class CLinker(link.Linker): ...@@ -526,10 +526,23 @@ class CLinker(link.Linker):
This might contain duplicates. 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]: for x in [y.type for y in self.variables] + [y.op for y in self.node_order]:
try: ret += x.c_compile_args() try: ret += x.c_compile_args()
except utils.MethodNotDefined: pass 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 return ret
def headers(self): def headers(self):
...@@ -703,16 +716,7 @@ class CLinker(link.Linker): ...@@ -703,16 +716,7 @@ class CLinker(link.Linker):
instantiate.customize.add_support_code(support_code) instantiate.customize.add_support_code(support_code)
instantiate.customize.add_support_code(self.struct_code) instantiate.customize.add_support_code(self.struct_code)
instantiate.customize.add_support_code(static) 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(): for arg in self.compile_args():
instantiate.customize.add_extra_compile_arg(arg) instantiate.customize.add_extra_compile_arg(arg)
for header in self.headers(): for header in self.headers():
......
...@@ -97,6 +97,25 @@ class CLinkerOp(object): ...@@ -97,6 +97,25 @@ class CLinkerOp(object):
raise utils.MethodNotDefined('%s.c_compile_args' \ raise utils.MethodNotDefined('%s.c_compile_args' \
% self.__class__.__name__) % 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): def c_headers(self):
"""Optional: Return a list of header files that must be included to compile the C code. """Optional: Return a list of header files that must be included to compile the C code.
......
...@@ -148,6 +148,24 @@ class CLinkerType(object): ...@@ -148,6 +148,24 @@ class CLinkerType(object):
""" """
raise MethodNotDefined("c_compile_args", type(self), self.__class__.__name__) 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): def c_headers(self):
"""Optional: Return a list of header files required by code returned by """Optional: Return a list of header files required by code returned by
this class. this class.
......
差异被折叠。
...@@ -2054,8 +2054,10 @@ class Reshape(Op): ...@@ -2054,8 +2054,10 @@ class Reshape(Op):
The number of dimensions to which to reshape to (ndim) must be known at graph The number of dimensions to which to reshape to (ndim) must be known at graph
build time.""" build time."""
view_map = {0: [0]} #output 0 is potentially aliased to inputs [0] 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 self.ndim = ndim
if name:
self.name = name
def __eq__(self, other): def __eq__(self, other):
return (type(other) is Reshape) and (other.ndim == self.ndim) return (type(other) is Reshape) and (other.ndim == self.ndim)
def __hash__(self): def __hash__(self):
...@@ -2075,10 +2077,10 @@ class Reshape(Op): ...@@ -2075,10 +2077,10 @@ class Reshape(Op):
def grad(self, (x, shp), (g_out,)): def grad(self, (x, shp), (g_out,)):
return [reshape(g_out, shape(x), ndim=x.ndim), None] 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: if ndim is None:
ndim = get_vector_length(newshape) ndim = get_vector_length(newshape)
op = Reshape(ndim) op = Reshape(ndim, name)
return op(x, newshape) return op(x, newshape)
......
...@@ -581,6 +581,11 @@ class CrossentropySoftmaxArgmax1HotWithBias(gof.Op): ...@@ -581,6 +581,11 @@ class CrossentropySoftmaxArgmax1HotWithBias(gof.Op):
""", """,
inside_row_loop, 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] nll_i[0] = - x_i[y_i*Sx]
- b_i[y_i*Sb] - b_i[y_i*Sb]
+ row_max + row_max
......
...@@ -686,7 +686,7 @@ class Canonizer(gof.LocalOptimizer): ...@@ -686,7 +686,7 @@ class Canonizer(gof.LocalOptimizer):
op = node.op op = node.op
if op not in [self.main, self.inverse, self.reciprocal]: if op not in [self.main, self.inverse, self.reciprocal]:
return False return False
inputs = node.inputs inputs = node.inputs
out = node.outputs[0] out = node.outputs[0]
assert len(node.outputs) == 1 assert len(node.outputs) == 1
...@@ -725,8 +725,14 @@ class Canonizer(gof.LocalOptimizer): ...@@ -725,8 +725,14 @@ class Canonizer(gof.LocalOptimizer):
return getattr(self, 'name', 'Canonizer(%s, %s, %s)' % (self.main, self.inverse, self.reciprocal)) return getattr(self, 'name', 'Canonizer(%s, %s, %s)' % (self.main, self.inverse, self.reciprocal))
def mul_calculate(num, denum, aslist = False): def mul_calculate(num, denum, aslist=False):
v = reduce(N.multiply, num, 1.0) / reduce(N.multiply, denum, 1.0) 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 aslist:
if N.all(v == 1): if N.all(v == 1):
return [] return []
......
...@@ -551,7 +551,8 @@ def test_naacl_model(iters_per_unsup=10, iters_per_sup=10, ...@@ -551,7 +551,8 @@ def test_naacl_model(iters_per_unsup=10, iters_per_sup=10,
s0 = str(m.finetuning_update(*(inputs + [targets]))) s0 = str(m.finetuning_update(*(inputs + [targets])))
print iters_per_sup * (i+1), s0 print iters_per_sup * (i+1), s0
if iters_per_sup == 10: 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(): def jtest_main():
from theano import gof from theano import gof
......
...@@ -57,9 +57,13 @@ class test_dimshuffle_lift(unittest.TestCase): ...@@ -57,9 +57,13 @@ class test_dimshuffle_lift(unittest.TestCase):
x, y, z = inputs([False]*1, [False]*2, [False]*3) x, y, z = inputs([False]*1, [False]*2, [False]*3)
e = x + y + z e = x + y + z
g = Env([x, y, z], [e]) 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) 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(): def test_add_canonizer_problem0():
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论