提交 4566125b authored 作者: Olivier Delalleau's avatar Olivier Delalleau

Merge pull request #226 from nouiz/remove_deprecated

Remove deprecated stuff
...@@ -312,15 +312,9 @@ class Mode(object): ...@@ -312,15 +312,9 @@ class Mode(object):
# string as the key # string as the key
FAST_COMPILE = Mode('py', 'fast_compile') FAST_COMPILE = Mode('py', 'fast_compile')
FAST_RUN = Mode('c|py', 'fast_run') FAST_RUN = Mode('c|py', 'fast_run')
FAST_RUN_NOGC = Mode("c|py_nogc", 'fast_run')
STABILIZE = Mode("c|py", OPT_STABILIZE)
# The strings 'FAST_RUN_NOGC' and 'STABILIZE' are deprecated,
# the modes in question should be built when needed.
predefined_modes = {'FAST_COMPILE': FAST_COMPILE, predefined_modes = {'FAST_COMPILE': FAST_COMPILE,
'FAST_RUN': FAST_RUN, 'FAST_RUN': FAST_RUN,
'FAST_RUN_NOGC':FAST_RUN_NOGC,
'STABILIZE': STABILIZE,
} }
instanciated_default_mode=None instanciated_default_mode=None
...@@ -355,17 +349,6 @@ def get_mode(orig_string): ...@@ -355,17 +349,6 @@ def get_mode(orig_string):
from profilemode import ProfileMode,prof_mode_instance_to_print from profilemode import ProfileMode,prof_mode_instance_to_print
ret = eval(string+'(linker=config.linker, optimizer=config.optimizer)') ret = eval(string+'(linker=config.linker, optimizer=config.optimizer)')
elif predefined_modes.has_key(string): elif predefined_modes.has_key(string):
# 'FAST_RUN_NOGC' and 'STABILIZE' are deprecated
if string == 'FAST_RUN_NOGC':
warnings.warn("Using the string 'FAST_RUN_NOGC' as a mode is "
"deprecated, you should use the object "
"Mode(linker='c|py_nogc') instead.",
stacklevel=5)
elif string == 'STABILIZE':
warnings.warn("Using the string 'STABILIZE' as a mode is "
"deprecated, you should use the object "
"Mode(optimizer='stabilize') instead.",
stacklevel=5)
ret = predefined_modes[string] ret = predefined_modes[string]
else: else:
raise Exception("No predefined mode exist for string: %s"%string) raise Exception("No predefined mode exist for string: %s"%string)
......
...@@ -26,6 +26,11 @@ AddConfigVar('cast_policy', ...@@ -26,6 +26,11 @@ AddConfigVar('cast_policy',
), ),
) )
# python 2.* define int / int to return int and int // int to return int.
# python 3* define int / int to return float and int // int to return int.
# numpy 1.6.1 behaves as python 2.*. I think we should not change it faster
# than numpy. When we will do the transition, we should create an int_warn
# and floatX_warn option.
AddConfigVar('int_division', AddConfigVar('int_division',
"What to do when one computes x / y, where both x and y are of " "What to do when one computes x / y, where both x and y are of "
"integer types", "integer types",
......
...@@ -12,12 +12,14 @@ If you want to use a scalar variable in a Theano graph, ...@@ -12,12 +12,14 @@ If you want to use a scalar variable in a Theano graph,
you probably want to use theano.tensor.[c,z,f,d,b,w,i,l,]scalar! you probably want to use theano.tensor.[c,z,f,d,b,w,i,l,]scalar!
""" """
import math, warnings import math
import warnings
from copy import copy from copy import copy
from itertools import imap from itertools import imap
import numpy, theano import numpy
import theano
from theano import gof from theano import gof
from theano.gof import Op, utils, Variable, Constant, Type, Apply, Env from theano.gof import Op, utils, Variable, Constant, Type, Apply, Env
from theano.gof.python25 import partial, all, any from theano.gof.python25 import partial, all, any
...@@ -32,6 +34,7 @@ class ComplexError(Exception): ...@@ -32,6 +34,7 @@ class ComplexError(Exception):
"""Raised if complex numbers are used in an unsupported operation.""" """Raised if complex numbers are used in an unsupported operation."""
pass pass
class IntegerDivisionError(Exception): class IntegerDivisionError(Exception):
"""Raised if someone tries to divide integers with '/' instead of '//'.""" """Raised if someone tries to divide integers with '/' instead of '//'."""
pass pass
...@@ -44,6 +47,7 @@ def upcast(dtype, *dtypes): ...@@ -44,6 +47,7 @@ def upcast(dtype, *dtypes):
# modified within `make_array`. # modified within `make_array`.
keep_float32 = [(config.cast_policy == 'numpy+floatX' and keep_float32 = [(config.cast_policy == 'numpy+floatX' and
config.floatX == 'float32')] config.floatX == 'float32')]
def make_array(dt): def make_array(dt):
if dt == 'float64': if dt == 'float64':
# There is an explicit float64 dtype: we cannot keep float32. # There is an explicit float64 dtype: we cannot keep float32.
...@@ -59,10 +63,11 @@ def upcast(dtype, *dtypes): ...@@ -59,10 +63,11 @@ def upcast(dtype, *dtypes):
return rval return rval
def as_scalar(x, name = None): def as_scalar(x, name=None):
if isinstance(x, gof.Apply): if isinstance(x, gof.Apply):
if len(x.outputs) != 1: if len(x.outputs) != 1:
raise ValueError("It is ambiguous which output of a multi-output Op has to be fetched.", x) raise ValueError("It is ambiguous which output of a multi-output"
" Op has to be fetched.", x)
else: else:
x = x.outputs[0] x = x.outputs[0]
if isinstance(x, Variable): if isinstance(x, Variable):
...@@ -76,9 +81,10 @@ def as_scalar(x, name = None): ...@@ -76,9 +81,10 @@ def as_scalar(x, name = None):
def constant(x): def constant(x):
# pass through numpy scalars, since they are already typed on purpose typically. # pass through numpy scalars, since they are already typed on
if hasattr(x,'dtype'): # purpose typically.
assert x.ndim==0 if hasattr(x, 'dtype'):
assert x.ndim == 0
return ScalarConstant(Scalar(str(x.dtype)), x) return ScalarConstant(Scalar(str(x.dtype)), x)
if isinstance(x, builtin_float): if isinstance(x, builtin_float):
for dtype in ['float32', 'float64']: for dtype in ['float32', 'float64']:
...@@ -114,52 +120,53 @@ class Scalar(Type): ...@@ -114,52 +120,53 @@ class Scalar(Type):
TODO: refactor to be named ScalarType for consistency with TensorType TODO: refactor to be named ScalarType for consistency with TensorType
""" """
def __init__(self, dtype): def __init__(self, dtype):
if dtype == 'floatX': if dtype == 'floatX':
dtype = config.floatX dtype = config.floatX
self.dtype = dtype self.dtype = dtype
self.dtype_specs() # error checking self.dtype_specs() # error checking
def filter(self, data, strict=False, allow_downcast=None): def filter(self, data, strict=False, allow_downcast=None):
py_type = self.dtype_specs()[0] py_type = self.dtype_specs()[0]
if strict and not isinstance(data, py_type): if strict and not isinstance(data, py_type):
raise TypeError("%s expected a %s, got %s of type %s" % (self, py_type, data, raise TypeError("%s expected a %s, got %s of type %s" % (
type(data)), self, py_type, data, type(data)), data)
data)
try: try:
converted_data = py_type(data) converted_data = py_type(data)
if (allow_downcast or if (allow_downcast or
(allow_downcast is None and (allow_downcast is None and
type(data) is float and type(data) is float and
self.dtype==theano.config.floatX) or self.dtype == theano.config.floatX) or
data == converted_data): data == converted_data):
return py_type(data) return py_type(data)
else: else:
raise TypeError('Value cannot accurately be converted to dtype (%s) and allow_downcast is not True' % self.dtype) raise TypeError('Value cannot accurately be converted to dtype'
' (%s) and allow_downcast is not True' % self.dtype)
except Exception, e: except Exception, e:
raise TypeError("Could not convert %s (value=%s) to %s" % (type(data), data, self.dtype), e) raise TypeError("Could not convert %s (value=%s) to %s" % (
type(data), data, self.dtype), e)
def values_eq_approx(self, a, b, tolerance = 1e-4): def values_eq_approx(self, a, b, tolerance=1e-4):
return abs(a - b) <= ((abs(a)+abs(b)) * tolerance) return abs(a - b) <= ((abs(a) + abs(b)) * tolerance)
def c_headers(self): def c_headers(self):
l=['<math.h>'] l = ['<math.h>']
l.append('<numpy/arrayscalars.h>') l.append('<numpy/arrayscalars.h>')
if config.lib.amdlibm: if config.lib.amdlibm:
l+=['<amdlibm.h>'] l += ['<amdlibm.h>']
return l return l
def c_libraries(self): def c_libraries(self):
l=[] l = []
if config.lib.amdlibm: if config.lib.amdlibm:
l+=['amdlibm'] l += ['amdlibm']
return l return l
def c_compile_args(self): def c_compile_args(self):
if config.lib.amdlibm: if config.lib.amdlibm:
return ['-DREPLACE_WITH_AMDLIBM'] return ['-DREPLACE_WITH_AMDLIBM']
else: return [] else:
return []
def __eq__(self, other): def __eq__(self, other):
return type(self) == type(other) and other.dtype == self.dtype return type(self) == type(other) and other.dtype == self.dtype
...@@ -169,11 +176,13 @@ class Scalar(Type): ...@@ -169,11 +176,13 @@ class Scalar(Type):
def dtype_specs(self): def dtype_specs(self):
try: try:
return {# dtype: (py_type, c_type, cls_name) return { # dtype: (py_type, c_type, cls_name)
'float32': (numpy.float32, 'npy_float32', 'Float32'), 'float32': (numpy.float32, 'npy_float32', 'Float32'),
'float64': (numpy.float64, 'npy_float64', 'Float64'), 'float64': (numpy.float64, 'npy_float64', 'Float64'),
'complex128': (numpy.complex128, 'theano_complex128', 'Complex128'), 'complex128': (numpy.complex128, 'theano_complex128',
'complex64': (numpy.complex64, 'theano_complex64', 'Complex64'), 'Complex128'),
'complex64': (numpy.complex64, 'theano_complex64',
'Complex64'),
'uint8': (numpy.uint8, 'npy_uint8', 'UInt8'), 'uint8': (numpy.uint8, 'npy_uint8', 'UInt8'),
'int8': (numpy.int8, 'npy_int8', 'Int8'), 'int8': (numpy.int8, 'npy_int8', 'Int8'),
'uint16': (numpy.uint16, 'npy_uint16', 'UInt16'), 'uint16': (numpy.uint16, 'npy_uint16', 'UInt16'),
...@@ -184,13 +193,14 @@ class Scalar(Type): ...@@ -184,13 +193,14 @@ class Scalar(Type):
'int64': (numpy.int64, 'npy_int64', 'Int64') 'int64': (numpy.int64, 'npy_int64', 'Int64')
}[self.dtype] }[self.dtype]
except KeyError: except KeyError:
raise TypeError("Unsupported dtype for %s: %s" % (self.__class__.__name__, self.dtype)) raise TypeError("Unsupported dtype for %s: %s" % (
self.__class__.__name__, self.dtype))
def upcast(self, *others): def upcast(self, *others):
return upcast(*[x.dtype for x in [self]+list(others)]) return upcast(*[x.dtype for x in [self] + list(others)])
def make_variable(self, name = None): def make_variable(self, name=None):
return ScalarVariable(self, name = name) return ScalarVariable(self, name=name)
def __str__(self): def __str__(self):
return str(self.dtype) return str(self.dtype)
...@@ -207,7 +217,7 @@ class Scalar(Type): ...@@ -207,7 +217,7 @@ class Scalar(Type):
return """ return """
%(dtype)s %(name)s; %(dtype)s %(name)s;
typedef %(dtype)s %(name)s_dtype; typedef %(dtype)s %(name)s_dtype;
""" % dict(name = name, dtype = self.dtype_specs()[1]) """ % dict(name=name, dtype=self.dtype_specs()[1])
def c_init(self, name, sub): def c_init(self, name, sub):
return """ return """
...@@ -225,9 +235,9 @@ class Scalar(Type): ...@@ -225,9 +235,9 @@ class Scalar(Type):
} }
PyArray_ScalarAsCtype(py_%(name)s, &%(name)s); PyArray_ScalarAsCtype(py_%(name)s, &%(name)s);
""" % dict(sub, """ % dict(sub,
name = name, name=name,
dtype = specs[1], dtype=specs[1],
pyarr_type = 'Py%sArrType_Type' % specs[2]) pyarr_type='Py%sArrType_Type' % specs[2])
def c_sync(self, name, sub): def c_sync(self, name, sub):
specs = self.dtype_specs() specs = self.dtype_specs()
...@@ -244,9 +254,9 @@ class Scalar(Type): ...@@ -244,9 +254,9 @@ class Scalar(Type):
} }
PyArrayScalar_ASSIGN(py_%(name)s, %(cls)s, %(name)s); PyArrayScalar_ASSIGN(py_%(name)s, %(cls)s, %(name)s);
""" % dict(sub, """ % dict(sub,
name = name, name=name,
dtype = specs[1], dtype=specs[1],
cls = specs[2]) cls=specs[2])
def c_cleanup(self, name, sub): def c_cleanup(self, name, sub):
return "" return ""
...@@ -255,7 +265,8 @@ class Scalar(Type): ...@@ -255,7 +265,8 @@ class Scalar(Type):
if self.dtype.startswith('complex'): if self.dtype.startswith('complex'):
cplx_types = ['theano_complex64', 'theano_complex128'] cplx_types = ['theano_complex64', 'theano_complex128']
real_types = ['npy_int8', 'npy_int16', 'npy_int32', 'npy_int64', 'npy_float32', 'npy_float64'] real_types = ['npy_int8', 'npy_int16', 'npy_int32', 'npy_int64',
'npy_float32', 'npy_float64']
# If the 'int' C type is not exactly the same as an existing # If the 'int' C type is not exactly the same as an existing
# 'npy_intX', some C code may not compile, e.g. when assigning # 'npy_intX', some C code may not compile, e.g. when assigning
# the value 0 (cast to 'int' in C) to a theano_complex64. # the value 0 (cast to 'int' in C) to a theano_complex64.
...@@ -325,7 +336,7 @@ class Scalar(Type): ...@@ -325,7 +336,7 @@ class Scalar(Type):
return ''' return '''
template <> %(mytype)s & %(mytype)s::operator=<%(othertype)s>(const %(othertype)s & y) template <> %(mytype)s & %(mytype)s::operator=<%(othertype)s>(const %(othertype)s & y)
{ this->real=y.real; this->imag=y.imag; return *this; } { this->real=y.real; this->imag=y.imag; return *this; }
''' % dict(mytype = mytype, othertype = othertype) ''' % dict(mytype=mytype, othertype=othertype)
operator_eq = ''.join(operator_eq_real(ctype, rtype) operator_eq = ''.join(operator_eq_real(ctype, rtype)
for ctype in cplx_types for ctype in cplx_types
...@@ -347,7 +358,7 @@ class Scalar(Type): ...@@ -347,7 +358,7 @@ class Scalar(Type):
const %(mytype)s operator+(const %(othertype)s &y, const %(mytype)s &x) const %(mytype)s operator+(const %(othertype)s &y, const %(mytype)s &x)
{ return %(mytype)s(x.real+y, x.imag); } { return %(mytype)s(x.real+y, x.imag); }
''' % dict(mytype = mytype, othertype = othertype) ''' % dict(mytype=mytype, othertype=othertype)
operator_plus = ''.join(operator_plus_real(ctype, rtype) operator_plus = ''.join(operator_plus_real(ctype, rtype)
for ctype in cplx_types for ctype in cplx_types
...@@ -360,7 +371,7 @@ class Scalar(Type): ...@@ -360,7 +371,7 @@ class Scalar(Type):
const %(mytype)s operator-(const %(othertype)s &y, const %(mytype)s &x) const %(mytype)s operator-(const %(othertype)s &y, const %(mytype)s &x)
{ return %(mytype)s(y-x.real, -x.imag); } { return %(mytype)s(y-x.real, -x.imag); }
''' % dict(mytype = mytype, othertype = othertype) ''' % dict(mytype=mytype, othertype=othertype)
operator_minus = ''.join(operator_minus_real(ctype, rtype) operator_minus = ''.join(operator_minus_real(ctype, rtype)
for ctype in cplx_types for ctype in cplx_types
...@@ -373,14 +384,14 @@ class Scalar(Type): ...@@ -373,14 +384,14 @@ class Scalar(Type):
const %(mytype)s operator*(const %(othertype)s &y, const %(mytype)s &x) const %(mytype)s operator*(const %(othertype)s &y, const %(mytype)s &x)
{ return %(mytype)s(x.real*y, x.imag*y); } { return %(mytype)s(x.real*y, x.imag*y); }
''' % dict(mytype = mytype, othertype = othertype) ''' % dict(mytype=mytype, othertype=othertype)
operator_mul = ''.join(operator_mul_real(ctype, rtype) operator_mul = ''.join(operator_mul_real(ctype, rtype)
for ctype in cplx_types for ctype in cplx_types
for rtype in real_types) for rtype in real_types)
return template % dict(nbits = 64, half_nbits = 32) \ return template % dict(nbits=64, half_nbits=32) \
+ template % dict(nbits = 128, half_nbits = 64) \ + template % dict(nbits=128, half_nbits=64) \
+ operator_eq \ + operator_eq \
+ operator_plus \ + operator_plus \
+ operator_minus \ + operator_minus \
...@@ -390,14 +401,20 @@ class Scalar(Type): ...@@ -390,14 +401,20 @@ class Scalar(Type):
return "" return ""
def c_code_cache_version(self): def c_code_cache_version(self):
return (10, numpy.__version__) # Use the correct type checking and conversion functions # Use the correct type checking and conversion functions
return (9, numpy.__version__) # Make operators work with 64 and 128 arguments at the same time return (10, numpy.__version__)
return (8, numpy.__version__) # put const around operators and added unary '-' operator # Make operators work with 64 and 128 arguments at the same time
# no need to put lib.amdlibm here as c_compile_args() are put in the key. return (9, numpy.__version__)
# put const around operators and added unary '-' operator
return (8, numpy.__version__)
# no need to put lib.amdlibm here as c_compile_args() are put
# in the key.
return (7,) # make complex c code optional return (7,) # make complex c code optional
return (6,) # added implemeentations of operators that work with scalar arguments return (6,) # added implemeentations of operators that work
return (5,) #added constructors to theano_complex class # with scalar arguments
return (4,) #explicit T given in specialization of operator= lines. This makes it compile with open64 return (5,) # added constructors to theano_complex class
return (4,) # explicit T given in specialization of operator=
# lines. This makes it compile with open64
int8 = Scalar('int8') int8 = Scalar('int8')
...@@ -422,6 +439,7 @@ discrete_types = int_types + uint_types ...@@ -422,6 +439,7 @@ discrete_types = int_types + uint_types
continuous_types = float_types + complex_types continuous_types = float_types + complex_types
all_types = discrete_types + continuous_types all_types = discrete_types + continuous_types
class _scalar_py_operators: class _scalar_py_operators:
#UNARY #UNARY
...@@ -465,9 +483,11 @@ class _scalar_py_operators: ...@@ -465,9 +483,11 @@ class _scalar_py_operators:
def __rmod__(self,other): return mod(other,self) def __rmod__(self,other): return mod(other,self)
def __rpow__(self,other): return pow(other,self) def __rpow__(self,other): return pow(other,self)
class ScalarVariable(_scalar_py_operators, Variable): class ScalarVariable(_scalar_py_operators, Variable):
pass pass
class ScalarConstant(_scalar_py_operators, Constant): class ScalarConstant(_scalar_py_operators, Constant):
pass pass
...@@ -499,26 +519,33 @@ complexs128 = _multi(complex128) ...@@ -499,26 +519,33 @@ complexs128 = _multi(complex128)
# necessary to use this same mechanism in other places as well in the future. # necessary to use this same mechanism in other places as well in the future.
class upcast_out(object): class upcast_out(object):
def __new__(self, *types): def __new__(self, *types):
return Scalar(dtype = Scalar.upcast(*types)), return Scalar(dtype=Scalar.upcast(*types)),
def upcast_out_no_complex(*types): def upcast_out_no_complex(*types):
if any([type in complex_types for type in types]): if any([type in complex_types for type in types]):
raise TypeError('complex type are not supported') raise TypeError('complex type are not supported')
return Scalar(dtype = Scalar.upcast(*types)), return Scalar(dtype=Scalar.upcast(*types)),
def same_out(type): def same_out(type):
return type, return type,
def same_out_float_only(type): def same_out_float_only(type):
if type not in float_types: if type not in float_types:
raise TypeError('only float type are supported') raise TypeError('only float type are supported')
return type, return type,
class transfer_type(gof.utils.object2): class transfer_type(gof.utils.object2):
def __init__(self, *transfer): def __init__(self, *transfer):
assert all(type(x) == int for x in transfer) assert all(type(x) == int for x in transfer)
self.transfer = transfer self.transfer = transfer
def __str__(self): def __str__(self):
return 'transfer_type{%s}'%self.transfer return 'transfer_type{%s}' % self.transfer
def __call__(self, *types): def __call__(self, *types):
upcast = upcast_out(*types) upcast = upcast_out(*types)
retval = [] retval = []
...@@ -529,23 +556,36 @@ class transfer_type(gof.utils.object2): ...@@ -529,23 +556,36 @@ class transfer_type(gof.utils.object2):
retval += [types[i]] retval += [types[i]]
return retval return retval
#return [upcast if i is None else types[i] for i in self.transfer] #return [upcast if i is None else types[i] for i in self.transfer]
def __eq__(self, other): def __eq__(self, other):
return type(self) == type(other) and self.transfer == other.transfer return type(self) == type(other) and self.transfer == other.transfer
def __hash__(self): def __hash__(self):
return hash(self.transfer) return hash(self.transfer)
class specific_out(gof.utils.object2): class specific_out(gof.utils.object2):
def __init__(self, *spec): def __init__(self, *spec):
self.spec = spec self.spec = spec
def __call__(self, *types): def __call__(self, *types):
return self.spec return self.spec
def __eq__(self, other): def __eq__(self, other):
return type(self) == type(other) and self.spec == other.spec return type(self) == type(other) and self.spec == other.spec
def __hash__(self): def __hash__(self):
return hash(self.spec) return hash(self.spec)
def int_out(*types): def int_out(*types):
return int64, return int64,
def float_out(*types): def float_out(*types):
return float64, return float64,
def upgrade_to_float(*types): def upgrade_to_float(*types):
""" """
Upgrade any int types to float32 or float64 to avoid losing any precision. Upgrade any int types to float32 or float64 to avoid losing any precision.
...@@ -555,6 +595,8 @@ def upgrade_to_float(*types): ...@@ -555,6 +595,8 @@ def upgrade_to_float(*types):
int32: float64, int32: float64,
int64: float64} int64: float64}
return Scalar(Scalar.upcast(*[conv.get(type, type) for type in types])), return Scalar(Scalar.upcast(*[conv.get(type, type) for type in types])),
def upgrade_to_float_no_complex(*types): def upgrade_to_float_no_complex(*types):
""" """
don't accept complex, otherwise call upgrade_to_float(). don't accept complex, otherwise call upgrade_to_float().
...@@ -563,31 +605,40 @@ def upgrade_to_float_no_complex(*types): ...@@ -563,31 +605,40 @@ def upgrade_to_float_no_complex(*types):
if type in complex_types: if type in complex_types:
raise TypeError('complex argument not supported') raise TypeError('complex argument not supported')
return upgrade_to_float(*types) return upgrade_to_float(*types)
def same_out_nocomplex(type): def same_out_nocomplex(type):
if type in complex_types: if type in complex_types:
raise TypeError('complex argument not supported') raise TypeError('complex argument not supported')
return type, return type,
def int_out_nocomplex(*types): def int_out_nocomplex(*types):
for type in types: for type in types:
if type in complex_types: if type in complex_types:
raise TypeError('complex argument not supported') raise TypeError('complex argument not supported')
return int64, return int64,
def float_out_nocomplex(*types): def float_out_nocomplex(*types):
for type in types: for type in types:
if type in complex_types: if type in complex_types:
raise TypeError('complex argument not supported') raise TypeError('complex argument not supported')
return float64, return float64,
class unary_out_lookup(gof.utils.object2): class unary_out_lookup(gof.utils.object2):
""" """
get a output_types_preference object by passing a dictionary: get a output_types_preference object by passing a dictionary:
unary_out_lookup({int8:int32, float32:complex128}) unary_out_lookup({int8:int32, float32:complex128})
The result is an op that maps in8 to int32 and float32 to complex128 and other input types The result is an op that maps in8 to int32 and float32 to
lead to a TypeError. complex128 and other input types lead to a TypeError.
""" """
def __init__(self, type_table): def __init__(self, type_table):
self.tbl = type_table self.tbl = type_table
def __call__(self, *types): def __call__(self, *types):
if len(types) == 1: if len(types) == 1:
types = types[0] types = types[0]
...@@ -599,10 +650,13 @@ class unary_out_lookup(gof.utils.object2): ...@@ -599,10 +650,13 @@ class unary_out_lookup(gof.utils.object2):
return rval return rval
else: else:
return [rval] return [rval]
def __eq__(self, other): def __eq__(self, other):
return type(self) == type(other) and self.tbl == other.tbl return type(self) == type(other) and self.tbl == other.tbl
def __hash__(self): def __hash__(self):
return hash(type(self)) # ignore hash of table return hash(type(self)) # ignore hash of table
def real_out(type): def real_out(type):
if type == complex64: if type == complex64:
...@@ -611,12 +665,13 @@ def real_out(type): ...@@ -611,12 +665,13 @@ def real_out(type):
return float64, return float64,
return type, return type,
class ScalarOp(Op): class ScalarOp(Op):
nin = -1 nin = -1
nout = 1 nout = 1
def __init__(self, output_types_preference = None, name = None): def __init__(self, output_types_preference=None, name=None):
self.name = name self.name = name
if output_types_preference is not None: if output_types_preference is not None:
if not callable(output_types_preference): if not callable(output_types_preference):
...@@ -646,7 +701,8 @@ class ScalarOp(Op): ...@@ -646,7 +701,8 @@ class ScalarOp(Op):
self.output_types_preference, self.nout, len(variables))) self.output_types_preference, self.nout, len(variables)))
return variables return variables
else: else:
raise NotImplementedError("Cannot calculate the output types for %s" % self) raise NotImplementedError(
"Cannot calculate the output types for %s" % self)
def perform(self, node, inputs, output_storage): def perform(self, node, inputs, output_storage):
if self.nout == 1: if self.nout == 1:
...@@ -658,25 +714,30 @@ class ScalarOp(Op): ...@@ -658,25 +714,30 @@ class ScalarOp(Op):
storage[0] = variable storage[0] = variable
def impl(self, *inputs): def impl(self, *inputs):
raise utils.MethodNotDefined("impl", type(self), self.__class__.__name__) raise utils.MethodNotDefined("impl", type(self),
self.__class__.__name__)
def grad(self, inputs, output_gradients): def grad(self, inputs, output_gradients):
raise utils.MethodNotDefined("grad", type(self), self.__class__.__name__) raise utils.MethodNotDefined("grad", type(self),
self.__class__.__name__)
def __eq__(self, other): def __eq__(self, other):
test = type(self) == type(other) \ test = type(self) == type(other) \
and getattr(self, 'output_types_preference', None) \ and getattr(self, 'output_types_preference', None) \
== getattr(other, 'output_types_preference', None) == getattr(other, 'output_types_preference', None)
return test return test
def __hash__(self): def __hash__(self):
return hash(type(self).__name__) ^ hash(getattr(self, 'output_types_preference', 0)) return hash(type(self).__name__) ^ hash(
getattr(self, 'output_types_preference', 0))
def __str__(self): def __str__(self):
if hasattr(self, 'name') and self.name: if hasattr(self, 'name') and self.name:
return self.name return self.name
else: else:
return "%s{%s}" % (self.__class__.__name__, ", ".join("%s=%s" % (k, v) for k, v in self.__dict__.items() if k != "name")) return "%s{%s}" % (self.__class__.__name__,
", ".join("%s=%s" % (k, v) for k, v in
self.__dict__.items() if k != "name"))
def c_code_cache_version(self): def c_code_cache_version(self):
return (3,) return (3,)
...@@ -685,6 +746,7 @@ class ScalarOp(Op): ...@@ -685,6 +746,7 @@ class ScalarOp(Op):
class UnaryScalarOp(ScalarOp): class UnaryScalarOp(ScalarOp):
nin = 1 nin = 1
class BinaryScalarOp(ScalarOp): class BinaryScalarOp(ScalarOp):
# One may define in subclasses the following fields: # One may define in subclasses the following fields:
# - `identity`: for an associative operation, identity corresponds to # - `identity`: for an associative operation, identity corresponds to
...@@ -702,76 +764,94 @@ class BinaryScalarOp(ScalarOp): ...@@ -702,76 +764,94 @@ class BinaryScalarOp(ScalarOp):
class LogicalComparison(BinaryScalarOp): class LogicalComparison(BinaryScalarOp):
def output_types(self, *input_dtypes): def output_types(self, *input_dtypes):
return [int8] return [int8]
def grad(self, inputs, output_gradients): def grad(self, inputs, output_gradients):
return [None, None] return [None, None]
class FixedLogicalComparison(UnaryScalarOp): class FixedLogicalComparison(UnaryScalarOp):
""" """
Comparison to a fixed value. Comparison to a fixed value.
""" """
def output_types(self, *input_dtypes): def output_types(self, *input_dtypes):
return [int8] return [int8]
def grad(self, inputs, output_gradients): def grad(self, inputs, output_gradients):
return [None] return [None]
class LT(LogicalComparison): class LT(LogicalComparison):
identity = False identity = False
commutative = False commutative = False
associative = False associative = False
def impl(self, x, y): def impl(self, x, y):
# built-in < don't support complex # built-in < don't support complex
return numpy.less(x, y) return numpy.less(x, y)
def c_code(self, node, name, (x, y), (z, ), sub): def c_code(self, node, name, (x, y), (z, ), sub):
if node.inputs[0].type in complex_types: if node.inputs[0].type in complex_types:
raise NotImplementedError() raise NotImplementedError()
return "%(z)s = (%(x)s < %(y)s);" % locals() return "%(z)s = (%(x)s < %(y)s);" % locals()
lt = LT() lt = LT()
class GT(LogicalComparison): class GT(LogicalComparison):
identity = False identity = False
commutative = False commutative = False
associative = False associative = False
def impl(self, x, y): def impl(self, x, y):
# built-in > don't support complex # built-in > don't support complex
return numpy.greater(x, y) return numpy.greater(x, y)
def c_code(self, node, name, (x, y), (z, ), sub): def c_code(self, node, name, (x, y), (z, ), sub):
if node.inputs[0].type in complex_types: if node.inputs[0].type in complex_types:
raise NotImplementedError() raise NotImplementedError()
return "%(z)s = (%(x)s > %(y)s);" % locals() return "%(z)s = (%(x)s > %(y)s);" % locals()
gt = GT() gt = GT()
class LE(LogicalComparison): class LE(LogicalComparison):
identity = False identity = False
commutative = False commutative = False
associative = False associative = False
def impl(self, x, y): def impl(self, x, y):
# built-in <= don't support complex # built-in <= don't support complex
return numpy.less_equal(x, y) return numpy.less_equal(x, y)
def c_code(self, node, name, (x, y), (z, ), sub): def c_code(self, node, name, (x, y), (z, ), sub):
if node.inputs[0].type in complex_types: if node.inputs[0].type in complex_types:
raise NotImplementedError() raise NotImplementedError()
return "%(z)s = (%(x)s <= %(y)s);" % locals() return "%(z)s = (%(x)s <= %(y)s);" % locals()
le = LE() le = LE()
class GE(LogicalComparison): class GE(LogicalComparison):
identity = False identity = False
commutative = False commutative = False
associative = False associative = False
def impl(self, x, y): def impl(self, x, y):
# built-in >= don't support complex # built-in >= don't support complex
return numpy.greater_equal(x, y) return numpy.greater_equal(x, y)
def c_code(self, node, name, (x, y), (z, ), sub): def c_code(self, node, name, (x, y), (z, ), sub):
if node.inputs[0].type in complex_types: if node.inputs[0].type in complex_types:
raise NotImplementedError() raise NotImplementedError()
return "%(z)s = (%(x)s >= %(y)s);" % locals() return "%(z)s = (%(x)s >= %(y)s);" % locals()
ge = GE() ge = GE()
class EQ(LogicalComparison): class EQ(LogicalComparison):
identity = False identity = False
commutative = True commutative = True
associative = False associative = False
def impl(self, x, y): def impl(self, x, y):
return x == y return x == y
def c_code(self, node, name, (x, y), (z, ), sub): def c_code(self, node, name, (x, y), (z, ), sub):
if node.inputs[0].type in complex_types: if node.inputs[0].type in complex_types:
raise NotImplementedError() raise NotImplementedError()
...@@ -783,8 +863,10 @@ class NEQ(LogicalComparison): ...@@ -783,8 +863,10 @@ class NEQ(LogicalComparison):
identity = False identity = False
commutative = True commutative = True
associative = False associative = False
def impl(self, x, y): def impl(self, x, y):
return x != y return x != y
def c_code(self, node, name, (x, y), (z, ), sub): def c_code(self, node, name, (x, y), (z, ), sub):
if node.inputs[0].type in complex_types: if node.inputs[0].type in complex_types:
raise NotImplementedError() raise NotImplementedError()
...@@ -795,6 +877,7 @@ neq = NEQ() ...@@ -795,6 +877,7 @@ neq = NEQ()
class IsNan(FixedLogicalComparison): class IsNan(FixedLogicalComparison):
def impl(self, x): def impl(self, x):
return numpy.isnan(x) return numpy.isnan(x)
def c_code(self, node, name, (x, ), (z, ), sub): def c_code(self, node, name, (x, ), (z, ), sub):
if node.inputs[0].type in complex_types: if node.inputs[0].type in complex_types:
raise NotImplementedError() raise NotImplementedError()
...@@ -805,6 +888,7 @@ isnan = IsNan() ...@@ -805,6 +888,7 @@ isnan = IsNan()
class IsInf(FixedLogicalComparison): class IsInf(FixedLogicalComparison):
def impl(self, x): def impl(self, x):
return numpy.isinf(x) return numpy.isinf(x)
def c_code(self, node, name, (x, ), (z, ), sub): def c_code(self, node, name, (x, ), (z, ), sub):
if node.inputs[0].type in complex_types: if node.inputs[0].type in complex_types:
raise NotImplementedError() raise NotImplementedError()
...@@ -817,9 +901,11 @@ isinf = IsInf() ...@@ -817,9 +901,11 @@ isinf = IsInf()
class InRange(LogicalComparison): class InRange(LogicalComparison):
nin = 3 nin = 3
def __init__(self, openlow, openhi): def __init__(self, openlow, openhi):
self.openlow = openlow self.openlow = openlow
self.openhi = openhi self.openhi = openhi
def impl(self, x, low, hi): def impl(self, x, low, hi):
if self.openlow and x <= low: if self.openlow and x <= low:
return False return False
...@@ -830,6 +916,7 @@ class InRange(LogicalComparison): ...@@ -830,6 +916,7 @@ class InRange(LogicalComparison):
elif not self.openhi and x > hi: elif not self.openhi and x > hi:
return False return False
return True return True
def c_code(self, node, name, (x, low, hi), (z, ), sub): def c_code(self, node, name, (x, low, hi), (z, ), sub):
if self.openlow: if self.openlow:
cmp1 = '>' cmp1 = '>'
...@@ -846,14 +933,18 @@ class InRange(LogicalComparison): ...@@ -846,14 +933,18 @@ class InRange(LogicalComparison):
#backport #backport
#cmp2 = '<' if self.openhi else '<=' #cmp2 = '<' if self.openhi else '<='
return "%(z)s = %(x)s %(cmp1)s %(low)s && %(x)s %(cmp2)s %(hi)s;" % locals() return ("%(z)s = %(x)s %(cmp1)s %(low)s &&"
" %(x)s %(cmp2)s %(hi)s;" % locals())
def grad(self, (x, low, hi), (gz, )): def grad(self, (x, low, hi), (gz, )):
return None, None, None return None, None, None
inopenrange = InRange(True, True) inopenrange = InRange(True, True)
inclosedrange = InRange(False, False) inclosedrange = InRange(False, False)
class Switch(ScalarOp): class Switch(ScalarOp):
nin = 3 nin = 3
def impl(self, cond, ift, iff): def impl(self, cond, ift, iff):
if cond: if cond:
return ift return ift
...@@ -864,6 +955,7 @@ class Switch(ScalarOp): ...@@ -864,6 +955,7 @@ class Switch(ScalarOp):
#return ift if cond else iff #return ift if cond else iff
def c_code(self, node, name, (cond, ift, iff), (z, ), sub): def c_code(self, node, name, (cond, ift, iff), (z, ), sub):
return "%(z)s = %(cond)s ? %(ift)s : %(iff)s;" % locals() return "%(z)s = %(cond)s ? %(ift)s : %(iff)s;" % locals()
def grad(self, (cond, ift, iff), (gz, )): def grad(self, (cond, ift, iff), (gz, )):
if ift.type in continuous_types: if ift.type in continuous_types:
first_part = switch(cond, gz, 0) first_part = switch(cond, gz, 0)
...@@ -885,125 +977,148 @@ switch = Switch() ...@@ -885,125 +977,148 @@ switch = Switch()
# BIT-WISE OPERATORS # BIT-WISE OPERATORS
#################### ####################
class UnaryBitOp(UnaryScalarOp): class UnaryBitOp(UnaryScalarOp):
def output_types(self, *input_types): def output_types(self, *input_types):
for i in input_types[0]: for i in input_types[0]:
if i not in (int8, int16, int32, int64): if i not in (int8, int16, int32, int64):
raise TypeError('input to a BitOp must have type int8, int16, int32 or int64... not %s' % i) raise TypeError('input to a BitOp must have type int8,'
' int16, int32 or int64... not %s' % i)
return upcast_out(*input_types[0]) return upcast_out(*input_types[0])
def grad(self, inputs, output_gradients): def grad(self, inputs, output_gradients):
return [None] return [None]
class BinaryBitOp(BinaryScalarOp): class BinaryBitOp(BinaryScalarOp):
def output_types(self, *input_types): def output_types(self, *input_types):
t0, t1 = input_types[0] t0, t1 = input_types[0]
for i in input_types[0]: for i in input_types[0]:
if i not in (int8, int16, int32, int64): if i not in (int8, int16, int32, int64):
raise TypeError('input to a BitOp must have type int8, int16, int32 or int64... not %s' % i) raise TypeError('input to a BitOp must have type int8,'
' int16, int32 or int64... not %s' % i)
return upcast_out(*input_types[0]) return upcast_out(*input_types[0])
def grad(self, inputs, output_gradients): def grad(self, inputs, output_gradients):
return [None, None] return [None, None]
class OR(BinaryBitOp): class OR(BinaryBitOp):
identity = 0 identity = 0
commutative = True commutative = True
associative = True associative = True
def impl(self, x, y): def impl(self, x, y):
return x | y return x | y
def c_code(self, node, name, (x, y), (z, ), sub): def c_code(self, node, name, (x, y), (z, ), sub):
return "%(z)s = (%(x)s | %(y)s);" % locals() return "%(z)s = (%(x)s | %(y)s);" % locals()
or_ = OR() or_ = OR()
class XOR(BinaryBitOp): class XOR(BinaryBitOp):
identity = 0 identity = 0
commutative = True commutative = True
associative = True associative = True
def impl(self, x, y): def impl(self, x, y):
return x ^ y return x ^ y
def c_code(self, node, name, (x, y), (z, ), sub): def c_code(self, node, name, (x, y), (z, ), sub):
return "%(z)s = (%(x)s ^ %(y)s);" % locals() return "%(z)s = (%(x)s ^ %(y)s);" % locals()
xor = XOR() xor = XOR()
class AND(BinaryBitOp): class AND(BinaryBitOp):
identity = 1 identity = 1
commutative = True commutative = True
associative = True associative = True
def impl(self, x, y): def impl(self, x, y):
return x & y return x & y
def c_code(self, node, name, (x, y), (z, ), sub): def c_code(self, node, name, (x, y), (z, ), sub):
return "%(z)s = (%(x)s & %(y)s);" % locals() return "%(z)s = (%(x)s & %(y)s);" % locals()
and_ = AND() and_ = AND()
class Invert(UnaryBitOp): class Invert(UnaryBitOp):
def impl(self, x): def impl(self, x):
return ~x return ~x
def c_code(self, node, name, (x,), (z, ), sub): def c_code(self, node, name, (x,), (z, ), sub):
return "%(z)s = (~%(x)s);" % locals() return "%(z)s = (~%(x)s);" % locals()
invert = Invert() invert = Invert()
############## ##############
# Arithmetic # Arithmetic
############## ##############
class Maximum(BinaryScalarOp): class Maximum(BinaryScalarOp):
commutative = True commutative = True
associative = True associative = True
def impl(self, *inputs): def impl(self, *inputs):
# The built-in max function don't support complex type # The built-in max function don't support complex type
return numpy.maximum(*inputs) return numpy.maximum(*inputs)
def c_code(self, node, name, (x,y), (z, ), sub):
def c_code(self, node, name, (x, y), (z, ), sub):
if any([i.type in complex_types for i in node.inputs]): if any([i.type in complex_types for i in node.inputs]):
raise NotImplementedError() raise NotImplementedError()
return "%(z)s = ((%(y)s)>(%(x)s)? (%(y)s):(%(x)s));" %locals() return "%(z)s = ((%(y)s)>(%(x)s)? (%(y)s):(%(x)s));" % locals()
def grad(self, (x, y), (gz, )): def grad(self, (x, y), (gz, )):
assert gz.type not in complex_types assert gz.type not in complex_types
# max is not defined for complex_types # max is not defined for complex_types
gx, gy = None, None gx, gy = None, None
if x.type in float_types: if x.type in float_types:
gx = eq(maximum(x,y), x)*gz gx = eq(maximum(x, y), x) * gz
if y.type in float_types: if y.type in float_types:
gy = eq(maximum(x,y), y)*gz gy = eq(maximum(x, y), y) * gz
return (gx,gy) return (gx, gy)
maximum = Maximum(upcast_out, name='maximum')
maximum = Maximum(upcast_out, name = 'maximum')
class Minimum(BinaryScalarOp): class Minimum(BinaryScalarOp):
commutative = True commutative = True
associative = True associative = True
def impl(self, *inputs): def impl(self, *inputs):
# The built-in min function don't support complex type # The built-in min function don't support complex type
return numpy.minimum(*inputs) return numpy.minimum(*inputs)
def c_code(self, node, name, (x,y), (z, ), sub):
def c_code(self, node, name, (x, y), (z, ), sub):
if any([i.type in complex_types for i in node.inputs]): if any([i.type in complex_types for i in node.inputs]):
raise NotImplementedError() raise NotImplementedError()
return "%(z)s = ((%(y)s)<(%(x)s)? (%(y)s):(%(x)s));" %locals() return "%(z)s = ((%(y)s)<(%(x)s)? (%(y)s):(%(x)s));" % locals()
def grad(self, (x, y), (gz, )): def grad(self, (x, y), (gz, )):
assert gz.type not in complex_types assert gz.type not in complex_types
# max is not defined for complex_types # max is not defined for complex_types
gx, gy = None, None gx, gy = None, None
if x.type in float_types: if x.type in float_types:
gx = eq(minimum(x,y), x)*gz gx = eq(minimum(x, y), x) * gz
if y.type in float_types: if y.type in float_types:
gy = eq(minimum(x,y), y)*gz gy = eq(minimum(x, y), y) * gz
return (gx,gy) return (gx, gy)
minimum = Minimum(upcast_out, name='minimum')
minimum = Minimum(upcast_out, name = 'minimum')
class Add(ScalarOp): class Add(ScalarOp):
identity = 0 identity = 0
commutative = True commutative = True
associative = True associative = True
def impl(self, *inputs): def impl(self, *inputs):
return sum(inputs) return sum(inputs)
def c_code(self, node, name, inputs, (z, ), sub): def c_code(self, node, name, inputs, (z, ), sub):
if not inputs: if not inputs:
return z + " = 0;" return z + " = 0;"
else: else:
return z + " = " + " + ".join(inputs) + ";" return z + " = " + " + ".join(inputs) + ";"
def grad(self, inputs, (gz, )): def grad(self, inputs, (gz, )):
retval = [] retval = []
if gz.type in complex_types: if gz.type in complex_types:
...@@ -1023,19 +1138,23 @@ class Add(ScalarOp): ...@@ -1023,19 +1138,23 @@ class Add(ScalarOp):
else: else:
retval += [None] * len(inputs) retval += [None] * len(inputs)
return retval return retval
add = Add(upcast_out, name = 'add') add = Add(upcast_out, name='add')
class Mul(ScalarOp): class Mul(ScalarOp):
identity = 1 identity = 1
commutative = True commutative = True
associative = True associative = True
def impl(self, *inputs): def impl(self, *inputs):
return numpy.product(inputs) return numpy.product(inputs)
def c_code(self, node, name, inputs, (z, ), sub): def c_code(self, node, name, inputs, (z, ), sub):
if not inputs: if not inputs:
return z + " = 1;" return z + " = 1;"
else: else:
return z + " = " + " * ".join(inputs) + ";" return z + " = " + " * ".join(inputs) + ";"
def grad(self, inputs, (gz, )): def grad(self, inputs, (gz, )):
retval = [] retval = []
for input in inputs: for input in inputs:
...@@ -1047,21 +1166,28 @@ class Mul(ScalarOp): ...@@ -1047,21 +1166,28 @@ class Mul(ScalarOp):
yr = real(otherprod) yr = real(otherprod)
yi = imag(otherprod) yi = imag(otherprod)
if input.type in complex_types: if input.type in complex_types:
retval += [complex(yr*real(gz)+yi*imag(gz), yr*imag(gz)-yi*real(gz))] retval += [complex(yr * real(gz) + yi * imag(gz),
yr * imag(gz) - yi * real(gz))]
else: else:
retval += [cast(yr*real(gz)+yi*imag(gz), input.type.dtype)] retval += [cast(yr * real(gz) + yi * imag(gz),
input.type.dtype)]
else: else:
retval += [cast(mul(*([gz] + utils.difference(inputs, [input]))), input.type.dtype)] retval += [cast(mul(*([gz] + utils.difference(inputs,
[input]))),
input.type.dtype)]
else: else:
retval += [None] retval += [None]
return retval return retval
mul = Mul(upcast_out, name = 'mul') mul = Mul(upcast_out, name='mul')
class Sub(BinaryScalarOp): class Sub(BinaryScalarOp):
def impl(self, x, y): def impl(self, x, y):
return x - y return x - y
def c_code(self, node, name, (x, y), (z, ), sub): def c_code(self, node, name, (x, y), (z, ), sub):
return "%(z)s = %(x)s - %(y)s;" % locals() return "%(z)s = %(x)s - %(y)s;" % locals()
def grad(self, (x, y), (gz, )): def grad(self, (x, y), (gz, )):
if gz.type in complex_types: if gz.type in complex_types:
raise NotImplementedError() raise NotImplementedError()
...@@ -1076,7 +1202,7 @@ class Sub(BinaryScalarOp): ...@@ -1076,7 +1202,7 @@ class Sub(BinaryScalarOp):
else: else:
second_part = None second_part = None
return first_part, second_part return first_part, second_part
sub = Sub(upcast_out, name = 'sub') sub = Sub(upcast_out, name='sub')
def int_or_true_div(x_discrete, y_discrete): def int_or_true_div(x_discrete, y_discrete):
...@@ -1132,6 +1258,7 @@ class TrueDiv(BinaryScalarOp): ...@@ -1132,6 +1258,7 @@ class TrueDiv(BinaryScalarOp):
return [Scalar(config.floatX)] return [Scalar(config.floatX)]
else: else:
return super(TrueDiv, self).output_types(types) return super(TrueDiv, self).output_types(types)
def impl(self, x, y): def impl(self, x, y):
x = numpy.asarray(x) x = numpy.asarray(x)
y = numpy.asarray(y) y = numpy.asarray(y)
...@@ -1139,14 +1266,17 @@ class TrueDiv(BinaryScalarOp): ...@@ -1139,14 +1266,17 @@ class TrueDiv(BinaryScalarOp):
return numpy.array(float(x) / y, dtype=config.floatX) return numpy.array(float(x) / y, dtype=config.floatX)
else: else:
return x / y return x / y
def c_code(self, node, name, (x, y), (z, ), sub): def c_code(self, node, name, (x, y), (z, ), sub):
#we generate good c code only when both are complex! # we generate good c code only when both are complex!
if sum([node.inputs[0].type in complex_types, node.inputs[1].type in complex_types])==1: if sum([node.inputs[0].type in complex_types,
node.inputs[1].type in complex_types]) == 1:
raise NotImplementedError('type not supported', type) raise NotImplementedError('type not supported', type)
if (node.inputs[0].type in discrete_types and if (node.inputs[0].type in discrete_types and
node.inputs[1].type in discrete_types): node.inputs[1].type in discrete_types):
return "%(z)s = ((double)%(x)s) / %(y)s;" % locals() return "%(z)s = ((double)%(x)s) / %(y)s;" % locals()
return "%(z)s = %(x)s / %(y)s;" % locals() return "%(z)s = %(x)s / %(y)s;" % locals()
def grad(self, (x, y), (gz, )): def grad(self, (x, y), (gz, )):
if x.type in complex_types: if x.type in complex_types:
raise NotImplementedError() raise NotImplementedError()
...@@ -1164,16 +1294,24 @@ class TrueDiv(BinaryScalarOp): ...@@ -1164,16 +1294,24 @@ class TrueDiv(BinaryScalarOp):
assert y.type in discrete_types assert y.type in discrete_types
second_part = None second_part = None
return first_part, second_part return first_part, second_part
true_div = TrueDiv(upcast_out, name = 'true_div') true_div = TrueDiv(upcast_out, name='true_div')
class IntDiv(BinaryScalarOp): class IntDiv(BinaryScalarOp):
def impl(self, x, y): def impl(self, x, y):
return x // y return x // y
def c_code(self, node, name, (x,y), (z,), sub):
raise NotImplementedError("For integer arguments the behavior of division in C and in Python [can] differ when the quotient is negative. C actually does not even specify a correct behaviour in this case, it is up to the chip.") def c_code(self, node, name, (x, y), (z,), sub):
raise NotImplementedError("For integer arguments the behavior of"
" division in C and in Python [can] differ"
" when the quotient is negative. C actually"
" does not even specify a correct behaviour"
" in this case, it is up to the chip.")
def grad(self, inputs, g_output): def grad(self, inputs, g_output):
return [None] * len(inputs) return [None] * len(inputs)
int_div = IntDiv(upcast_out, name = 'int_div') int_div = IntDiv(upcast_out, name='int_div')
floor_div = int_div floor_div = int_div
...@@ -1211,30 +1349,30 @@ class Mod(BinaryScalarOp): ...@@ -1211,30 +1349,30 @@ class Mod(BinaryScalarOp):
""" """
# raise NotImplementedError("Unlike Python, C's modulo returns negative # raise NotImplementedError("Unlike Python, C's modulo returns negative
# modulo on negative dividend (to implement)") # modulo on negative dividend (to implement)")
t = node.inputs[0].type.upcast(*[ i.type for i in node.inputs[1:]]) t = node.inputs[0].type.upcast(*[i.type for i in node.inputs[1:]])
if (str(t) in imap(str, discrete_types) or if (str(t) in imap(str, discrete_types) or
t in ['uint8','int8','uint16','int16'] or t in ['uint8', 'int8', 'uint16', 'int16'] or
t in ['uint32','int32','uint64','int64'] or t in ['uint32', 'int32', 'uint64', 'int64'] or
t in discrete_types): t in discrete_types):
# The above or's should not be needed anymore. However, for now we # The above or's should not be needed anymore. However, for now we
# keep them out of safety, and verify they are useless with an # keep them out of safety, and verify they are useless with an
# assert. # assert.
assert str(t) in imap(str, discrete_types) assert str(t) in imap(str, discrete_types)
x_mod_y = "THEANO_MACRO_MOD(%(x)s, %(y)s)"%locals() x_mod_y = "THEANO_MACRO_MOD(%(x)s, %(y)s)" % locals()
x_mod_ymm = "THEANO_MACRO_MOD(-%(x)s, -%(y)s)"%locals() x_mod_ymm = "THEANO_MACRO_MOD(-%(x)s, -%(y)s)" % locals()
x_mod_ypm = "THEANO_MACRO_MOD(%(x)s, -%(y)s)"%locals() x_mod_ypm = "THEANO_MACRO_MOD(%(x)s, -%(y)s)" % locals()
x_mod_ymp = "THEANO_MACRO_MOD(-%(x)s, %(y)s)"%locals() x_mod_ymp = "THEANO_MACRO_MOD(-%(x)s, %(y)s)" % locals()
elif (str(t) in imap(str, float_types) or elif (str(t) in imap(str, float_types) or
t in ['float32','float64'] or t in ['float32', 'float64'] or
t in float_types): t in float_types):
# The above or's should not be needed anymore. However, for now we # The above or's should not be needed anymore. However, for now we
# keep them out of safety, and verify they are useless with an # keep them out of safety, and verify they are useless with an
# assert. # assert.
assert str(t) in imap(str, float_types) assert str(t) in imap(str, float_types)
x_mod_y = "fmod(%(x)s,%(y)s)"%locals() x_mod_y = "fmod(%(x)s,%(y)s)" % locals()
x_mod_ymm = "fmod(-%(x)s,-%(y)s)"%locals() x_mod_ymm = "fmod(-%(x)s,-%(y)s)" % locals()
x_mod_ypm = "fmod(%(x)s,-%(y)s)"%locals() x_mod_ypm = "fmod(%(x)s,-%(y)s)" % locals()
x_mod_ymp = "fmod(-%(x)s,%(y)s)"%locals() x_mod_ymp = "fmod(-%(x)s,%(y)s)" % locals()
elif str(t) in imap(str, complex_types): elif str(t) in imap(str, complex_types):
raise self.complex_error raise self.complex_error
else: else:
...@@ -1252,37 +1390,44 @@ if (%(x)s < 0){ ...@@ -1252,37 +1390,44 @@ if (%(x)s < 0){
}else{ }else{
%(z)s = %(x_mod_y)s; %(z)s = %(x_mod_y)s;
} }
"""%locals() """ % locals()
def grad(self, (x, y), (gz, )): def grad(self, (x, y), (gz, )):
return None, None return None, None
mod = Mod(upcast_out, name = 'mod') mod = Mod(upcast_out, name='mod')
class Pow(BinaryScalarOp): class Pow(BinaryScalarOp):
def impl(self, x, y): def impl(self, x, y):
return x ** y return x ** y
def c_code(self, node, name, (x, y), (z, ), sub): def c_code(self, node, name, (x, y), (z, ), sub):
if node.inputs[0].type in complex_types or node.inputs[1].type in complex_types: if (node.inputs[0].type in complex_types or
node.inputs[1].type in complex_types):
raise NotImplementedError('type not supported', type) raise NotImplementedError('type not supported', type)
return "%(z)s = pow(%(x)s, %(y)s);" % locals() return "%(z)s = pow(%(x)s, %(y)s);" % locals()
def grad(self, (x, y), (gz, )): def grad(self, (x, y), (gz, )):
if gz.type in complex_types: if gz.type in complex_types:
raise NotImplementedError() raise NotImplementedError()
if x.type in float_types: if x.type in float_types:
first_part = gz * y * x**(y - 1) first_part = gz * y * x ** (y - 1)
else: else:
first_part = None first_part = None
if y.type in float_types: if y.type in float_types:
second_part = gz * log(x) * x**y second_part = gz * log(x) * x ** y
else: else:
second_part = None second_part = None
return (first_part, second_part) return (first_part, second_part)
pow = Pow(upcast_out, name = 'pow') pow = Pow(upcast_out, name='pow')
class Clip(ScalarOp): class Clip(ScalarOp):
nin = 3 nin = 3
def impl(self, x, min, max): def impl(self, x, min, max):
if x < min: if x < min:
return min return min
...@@ -1290,8 +1435,10 @@ class Clip(ScalarOp): ...@@ -1290,8 +1435,10 @@ class Clip(ScalarOp):
return max return max
else: else:
return x return x
def c_code(self, node, name, (x, min, max), (z, ), sub): def c_code(self, node, name, (x, min, max), (z, ), sub):
return "%(z)s = %(x)s < %(min)s ? %(min)s : %(x)s > %(max)s ? %(max)s : %(x)s;" % locals() return "%(z)s = %(x)s < %(min)s ? %(min)s : %(x)s > %(max)s ? %(max)s : %(x)s;" % locals()
def grad(self, (x, min, max), (gz, )): def grad(self, (x, min, max), (gz, )):
assert gz.type not in complex_types assert gz.type not in complex_types
gx = ((x > min) & (x < max)) * gz gx = ((x > min) & (x < max)) * gz
...@@ -1301,34 +1448,38 @@ class Clip(ScalarOp): ...@@ -1301,34 +1448,38 @@ class Clip(ScalarOp):
return None, None, None return None, None, None
# Don't allow complex even if numpy do # Don't allow complex even if numpy do
# As there is no mathematical reason for this function on complex # As there is no mathematical reason for this function on complex
clip = Clip(upcast_out_no_complex, name = 'clip') clip = Clip(upcast_out_no_complex, name='clip')
class Second(BinaryScalarOp): class Second(BinaryScalarOp):
def impl(self, x, y): def impl(self, x, y):
return y return y
def c_code(self, node, name, (x, y), (z, ), sub): def c_code(self, node, name, (x, y), (z, ), sub):
return "%(z)s = %(y)s;" % locals() return "%(z)s = %(y)s;" % locals()
def grad(self, (x, y), (gz, )): def grad(self, (x, y), (gz, )):
if y.type in continuous_types: if y.type in continuous_types:
return None, gz return None, gz
else: else:
return None, None return None, None
second = Second(transfer_type(1), name='second')
second = Second(transfer_type(1), name = 'second')
class Identity(UnaryScalarOp): class Identity(UnaryScalarOp):
def impl(self, input): def impl(self, input):
return input return input
def c_code(self, node, name, (x, ), (z, ), sub): def c_code(self, node, name, (x, ), (z, ), sub):
return "%(z)s = %(x)s;" % locals() return "%(z)s = %(x)s;" % locals()
def grad(self, (x, ), (gz, )): def grad(self, (x, ), (gz, )):
if x.type in continuous_types: if x.type in continuous_types:
return gz, return gz,
else: else:
return None, return None,
identity = Identity(same_out, name = 'identity') identity = Identity(same_out, name='identity')
#### CASTING OPERATIONS #### CASTING OPERATIONS
class Cast(UnaryScalarOp): class Cast(UnaryScalarOp):
...@@ -1338,17 +1489,22 @@ class Cast(UnaryScalarOp): ...@@ -1338,17 +1489,22 @@ class Cast(UnaryScalarOp):
super(Cast, self).__init__(specific_out(o_type), name=name) super(Cast, self).__init__(specific_out(o_type), name=name)
self.o_type = o_type self.o_type = o_type
self.ctor = getattr(numpy, o_type.dtype) self.ctor = getattr(numpy, o_type.dtype)
def __str__(self): def __str__(self):
return '%s{%s}' % (self.__class__.__name__, self.o_type.dtype) return '%s{%s}' % (self.__class__.__name__, self.o_type.dtype)
def impl(self, input): def impl(self, input):
return self.ctor(input) return self.ctor(input)
def c_code(self, node, name, (x, ), (z, ), sub): def c_code(self, node, name, (x, ), (z, ), sub):
return "%s = (%s)%s;" % (z, node.outputs[0].type.dtype_specs()[1], x) return "%s = (%s)%s;" % (z, node.outputs[0].type.dtype_specs()[1], x)
def grad(self, (x, ), (gz, )): def grad(self, (x, ), (gz, )):
if x.type in continuous_types and self.o_type in continuous_types: if x.type in continuous_types and self.o_type in continuous_types:
return [cast(gz, x.type.dtype)] return [cast(gz, x.type.dtype)]
else: else:
return None, return None,
def c_code_cache_version(self): def c_code_cache_version(self):
s = super(Cast, self).c_code_cache_version() s = super(Cast, self).c_code_cache_version()
if s: if s:
...@@ -1382,17 +1538,22 @@ _cast_mapping = { ...@@ -1382,17 +1538,22 @@ _cast_mapping = {
'float64': convert_to_float64, 'float64': convert_to_float64,
'complex64': convert_to_complex64, 'complex64': convert_to_complex64,
'complex128': convert_to_complex128} 'complex128': convert_to_complex128}
def cast(x, dtype): def cast(x, dtype):
"""Symbolically cast `x` to a Scalar of given `dtype`.""" """Symbolically cast `x` to a Scalar of given `dtype`."""
if dtype == 'floatX': dtype = config.floatX if dtype == 'floatX':
dtype = config.floatX
_x = as_scalar(x) _x = as_scalar(x)
if _x.type.dtype == dtype: if _x.type.dtype == dtype:
return _x return _x
if _x.type.dtype.startswith('complex') and not dtype.startswith('complex'): if _x.type.dtype.startswith('complex') and not dtype.startswith('complex'):
raise TypeError('Casting from complex to real is ambiguous: consider real(), imag(), angle() or abs()') raise TypeError('Casting from complex to real is ambiguous: consider'
' real(), imag(), angle() or abs()')
return _cast_mapping[dtype](_x) return _cast_mapping[dtype](_x)
class Abs(UnaryScalarOp): class Abs(UnaryScalarOp):
def make_node(self, x): def make_node(self, x):
inputs = [as_scalar(input) for input in [x]] inputs = [as_scalar(input) for input in [x]]
...@@ -1401,15 +1562,19 @@ class Abs(UnaryScalarOp): ...@@ -1401,15 +1562,19 @@ class Abs(UnaryScalarOp):
elif inputs[0].type == complex128: elif inputs[0].type == complex128:
outputs = [float64()] outputs = [float64()]
else: else:
outputs = [t() for t in self.output_types([input.type for input in inputs])] outputs = [t() for t in self.output_types(
[input.type for input in inputs])]
return Apply(self, inputs, outputs) return Apply(self, inputs, outputs)
def impl(self, x): def impl(self, x):
return numpy.abs(x) return numpy.abs(x)
def grad(self, (x, ), (gz, )): def grad(self, (x, ), (gz, )):
if x.type in float_types + complex_types: if x.type in float_types + complex_types:
return gz * x / abs(x), # formula works for complex and real return gz * x / abs(x), # formula works for complex and real
else: else:
return None, return None,
def c_code(self, node, name, (x, ), (z, ), sub): def c_code(self, node, name, (x, ), (z, ), sub):
type = node.inputs[0].type type = node.inputs[0].type
if type in int_types: if type in int_types:
...@@ -1421,12 +1586,15 @@ class Abs(UnaryScalarOp): ...@@ -1421,12 +1586,15 @@ class Abs(UnaryScalarOp):
raise NotImplementedError('type not supported', type) raise NotImplementedError('type not supported', type)
abs_ = Abs(same_out) abs_ = Abs(same_out)
class Sgn(UnaryScalarOp): class Sgn(UnaryScalarOp):
def impl(self, x): def impl(self, x):
#casting to output type is handled by filter #casting to output type is handled by filter
return numpy.sign(x) return numpy.sign(x)
def grad(self, (x, ), (gz, )): def grad(self, (x, ), (gz, )):
return None, return None,
def c_code(self, node, name, (x, ), (z, ), sub): def c_code(self, node, name, (x, ), (z, ), sub):
#casting is done by compiler #casting is done by compiler
#TODO: use copysign #TODO: use copysign
...@@ -1435,32 +1603,40 @@ class Sgn(UnaryScalarOp): ...@@ -1435,32 +1603,40 @@ class Sgn(UnaryScalarOp):
return "%(z)s = (%(x)s >= 0) ? (%(x)s == 0) ? 0.0 : 1.0 : -1.0;" % locals() return "%(z)s = (%(x)s >= 0) ? (%(x)s == 0) ? 0.0 : 1.0 : -1.0;" % locals()
if type in int_types: if type in int_types:
return "%(z)s = (%(x)s >= 0) ? (%(x)s == 0) ? 0 : 1 : -1;" % locals() return "%(z)s = (%(x)s >= 0) ? (%(x)s == 0) ? 0 : 1 : -1;" % locals()
raise TypeError() #complex has no sgn raise TypeError() # complex has no sgn
def c_code_cache_version(self): def c_code_cache_version(self):
s = super(Sgn, self).c_code_cache_version() s = super(Sgn, self).c_code_cache_version()
if s: if s:
return (3,) + s return (3,) + s
else: #if parent is unversioned, we are too else: # if parent is unversioned, we are too
return s return s
sgn = Sgn(same_out_nocomplex, name = 'sgn') sgn = Sgn(same_out_nocomplex, name='sgn')
class Ceil(UnaryScalarOp): class Ceil(UnaryScalarOp):
def impl(self, x): def impl(self, x):
return numpy.ceil(x) return numpy.ceil(x)
def grad(self, (x,), (gz,)): def grad(self, (x,), (gz,)):
return None, return None,
def c_code(self, node, name, (x,), (z,), sub): def c_code(self, node, name, (x,), (z,), sub):
return "%(z)s = ceil(%(x)s);" % locals() return "%(z)s = ceil(%(x)s);" % locals()
ceil = Ceil(same_out_nocomplex, name = 'ceil') ceil = Ceil(same_out_nocomplex, name='ceil')
class Floor(UnaryScalarOp): class Floor(UnaryScalarOp):
def impl(self, x): def impl(self, x):
return numpy.floor(x) return numpy.floor(x)
def grad(self, (x,), (gz,)): def grad(self, (x,), (gz,)):
return None, return None,
def c_code(self, node, name, (x,), (z,), sub): def c_code(self, node, name, (x,), (z,), sub):
return "%(z)s = floor(%(x)s);" % locals() return "%(z)s = floor(%(x)s);" % locals()
floor = Floor(same_out_nocomplex, name = 'floor') floor = Floor(same_out_nocomplex, name='floor')
class RoundHalfToEven(UnaryScalarOp): class RoundHalfToEven(UnaryScalarOp):
""" """
...@@ -1471,6 +1647,7 @@ class RoundHalfToEven(UnaryScalarOp): ...@@ -1471,6 +1647,7 @@ class RoundHalfToEven(UnaryScalarOp):
""" """
def impl(self, x): def impl(self, x):
return numpy.round(x) return numpy.round(x)
def c_code___(self, node, name, (x, ), (z, ), sub): def c_code___(self, node, name, (x, ), (z, ), sub):
typ = node.outputs[0].type.dtype typ = node.outputs[0].type.dtype
if not node.outputs[0].type.dtype in ['float32', 'float64']: if not node.outputs[0].type.dtype in ['float32', 'float64']:
...@@ -1523,23 +1700,28 @@ class RoundHalfToEven(UnaryScalarOp): ...@@ -1523,23 +1700,28 @@ class RoundHalfToEven(UnaryScalarOp):
""" """
round_half_to_even = RoundHalfToEven(same_out_float_only) round_half_to_even = RoundHalfToEven(same_out_float_only)
def round_half_away_from_zero_(a): def round_half_away_from_zero_(a):
if a>0: if a > 0:
return numpy.floor(a + 0.5) return numpy.floor(a + 0.5)
else: else:
return numpy.ceil(a - 0.5) return numpy.ceil(a - 0.5)
round_half_away_from_zero_vec64 = numpy.vectorize(round_half_away_from_zero_, round_half_away_from_zero_vec64 = numpy.vectorize(
doc='round_half_away_from_zero_vec64') round_half_away_from_zero_,
round_half_away_from_zero_vec32 = numpy.vectorize(round_half_away_from_zero_, doc='round_half_away_from_zero_vec64')
doc='round_half_away_from_zero_vec32', round_half_away_from_zero_vec32 = numpy.vectorize(
otypes=['float32']) round_half_away_from_zero_,
doc='round_half_away_from_zero_vec32',
otypes=['float32'])
def round_half_away_from_zero_vec(a): def round_half_away_from_zero_vec(a):
if getattr(a, 'dtype',None) == numpy.float32: if getattr(a, 'dtype', None) == numpy.float32:
return round_half_away_from_zero_vec32(a) return round_half_away_from_zero_vec32(a)
return round_half_away_from_zero_vec64(a) return round_half_away_from_zero_vec64(a)
class RoundHalfAwayFromZero(UnaryScalarOp): class RoundHalfAwayFromZero(UnaryScalarOp):
""" """
Implement the same rounding algo as c round() fct. Implement the same rounding algo as c round() fct.
...@@ -1550,6 +1732,7 @@ class RoundHalfAwayFromZero(UnaryScalarOp): ...@@ -1550,6 +1732,7 @@ class RoundHalfAwayFromZero(UnaryScalarOp):
def impl(self, x): def impl(self, x):
return round_half_away_from_zero_vec(x) return round_half_away_from_zero_vec(x)
def c_code(self, node, name, (x, ), (z, ), sub): def c_code(self, node, name, (x, ), (z, ), sub):
if node.outputs[0].type.dtype in ['float32', 'float64']: if node.outputs[0].type.dtype in ['float32', 'float64']:
return "%(z)s = round(%(x)s);" % locals() return "%(z)s = round(%(x)s);" % locals()
...@@ -1557,58 +1740,69 @@ class RoundHalfAwayFromZero(UnaryScalarOp): ...@@ -1557,58 +1740,69 @@ class RoundHalfAwayFromZero(UnaryScalarOp):
Exception("The output should be float32 or float64") Exception("The output should be float32 or float64")
round_half_away_from_zero = RoundHalfAwayFromZero(same_out_float_only) round_half_away_from_zero = RoundHalfAwayFromZero(same_out_float_only)
class Neg(UnaryScalarOp): class Neg(UnaryScalarOp):
def impl(self, x): def impl(self, x):
return -x return -x
def grad(self, (x, ), (gz, )):
def grad(self, (x,), (gz,)):
if x.type in continuous_types: if x.type in continuous_types:
return -gz, return -gz,
else: else:
return None, return None,
def c_code(self, node, name, (x, ), (z, ), sub):
def c_code(self, node, name, (x,), (z,), sub):
return "%(z)s = -%(x)s;" % locals() return "%(z)s = -%(x)s;" % locals()
neg = Neg(same_out, name = 'neg') neg = Neg(same_out, name='neg')
class Inv(UnaryScalarOp): class Inv(UnaryScalarOp):
""" multiplicative inverse. Also called reciprocal""" """ multiplicative inverse. Also called reciprocal"""
def impl(self, x): def impl(self, x):
return 1.0 / x return 1.0 / x
def grad(self, (x, ), (gz, )):
def grad(self, (x,), (gz,)):
if x.type in complex_types: if x.type in complex_types:
raise NotImplementedError() raise NotImplementedError()
if x.type in float_types: if x.type in float_types:
return -gz / (x * x), return -gz / (x * x),
else: else:
return None, return None,
def c_code(self, node, name, (x, ), (z, ), sub):
def c_code(self, node, name, (x,), (z,), sub):
return "%(z)s = 1.0 / %(x)s;" % locals() return "%(z)s = 1.0 / %(x)s;" % locals()
inv = Inv(upgrade_to_float, name = 'inv') inv = Inv(upgrade_to_float, name='inv')
class Log(UnaryScalarOp): class Log(UnaryScalarOp):
""" log base e """ """ log base e """
def impl(self, x): def impl(self, x):
return numpy.log(x) return numpy.log(x)
def grad(self, (x, ), (gz, )):
def grad(self, (x,), (gz,)):
if x.type in complex_types: if x.type in complex_types:
raise NotImplementedError() raise NotImplementedError()
if x.type in float_types: if x.type in float_types:
return gz / x, return gz / x,
else: else:
return None, return None,
def c_code(self, node, name, (x, ), (z, ), sub):
def c_code(self, node, name, (x,), (z,), sub):
#todo: the version using log2 seems to be very slightly faster #todo: the version using log2 seems to be very slightly faster
# on some machines for some reason, check if it's worth switching # on some machines for some reason, check if it's worth switching
#return "%(z)s = log2(%(x)s) * 0.69314718055994529;" % locals() #return "%(z)s = log2(%(x)s) * 0.69314718055994529;" % locals()
if node.inputs[0].type in complex_types: if node.inputs[0].type in complex_types:
raise NotImplementedError('type not supported', type) raise NotImplementedError('type not supported', type)
return "%(z)s = log(%(x)s);" % locals() return "%(z)s = log(%(x)s);" % locals()
log = Log(upgrade_to_float, name = 'log') log = Log(upgrade_to_float, name='log')
class Log2(UnaryScalarOp): class Log2(UnaryScalarOp):
""" log base 2 """ """ log base 2 """
def impl(self, x): def impl(self, x):
return numpy.log2(x) return numpy.log2(x)
def grad(self, (x, ), (gz, )):
def grad(self, (x,), (gz,)):
if x.type in complex_types: if x.type in complex_types:
raise NotImplementedError() raise NotImplementedError()
if x.type in float_types: if x.type in float_types:
...@@ -1620,13 +1814,15 @@ class Log2(UnaryScalarOp): ...@@ -1620,13 +1814,15 @@ class Log2(UnaryScalarOp):
if node.inputs[0].type in complex_types: if node.inputs[0].type in complex_types:
raise NotImplementedError('type not supported', type) raise NotImplementedError('type not supported', type)
return "%(z)s = log2(%(x)s);" % locals() return "%(z)s = log2(%(x)s);" % locals()
log2 = Log2(upgrade_to_float, name = 'log2') log2 = Log2(upgrade_to_float, name='log2')
class Log10(UnaryScalarOp): class Log10(UnaryScalarOp):
""" log base 10 """ """ log base 10 """
def impl(self, x): def impl(self, x):
return numpy.log10(x) return numpy.log10(x)
def grad(self, (x, ), (gz, )):
def grad(self, (x,), (gz,)):
if x.type in complex_types: if x.type in complex_types:
raise NotImplementedError() raise NotImplementedError()
if x.type in float_types: if x.type in float_types:
...@@ -1638,27 +1834,32 @@ class Log10(UnaryScalarOp): ...@@ -1638,27 +1834,32 @@ class Log10(UnaryScalarOp):
if node.inputs[0].type in complex_types: if node.inputs[0].type in complex_types:
raise NotImplementedError('type not supported', type) raise NotImplementedError('type not supported', type)
return "%(z)s = log10(%(x)s);" % locals() return "%(z)s = log10(%(x)s);" % locals()
log10 = Log10(upgrade_to_float, name = 'log10') log10 = Log10(upgrade_to_float, name='log10')
class Log1p(UnaryScalarOp): class Log1p(UnaryScalarOp):
""" log(1+x) """ """ log(1+x) """
def impl(self, x): def impl(self, x):
return numpy.log1p(x) return numpy.log1p(x)
def grad(self, (x,), (gz,)): def grad(self, (x,), (gz,)):
if gz.type in complex_types: if gz.type in complex_types:
raise NotImplementedError() raise NotImplementedError()
if gz.type in float_types: if gz.type in float_types:
return [gz / (1+x)] return [gz / (1 + x)]
return [None] return [None]
def c_code(self, node, name, (x, ), (z, ), sub): def c_code(self, node, name, (x, ), (z, ), sub):
if node.inputs[0].type in complex_types: if node.inputs[0].type in complex_types:
raise NotImplementedError('type not supported', type) raise NotImplementedError('type not supported', type)
return "%(z)s = log1p(%(x)s);" % locals() return "%(z)s = log1p(%(x)s);" % locals()
log1p = Log1p(upgrade_to_float, name = 'log1p') log1p = Log1p(upgrade_to_float, name='log1p')
class Exp(UnaryScalarOp): class Exp(UnaryScalarOp):
def impl(self, x): def impl(self, x):
return numpy.exp(x) return numpy.exp(x)
def grad(self, (x, ), (gz, )): def grad(self, (x, ), (gz, )):
if x.type in complex_types: if x.type in complex_types:
raise NotImplementedError() raise NotImplementedError()
...@@ -1666,15 +1867,18 @@ class Exp(UnaryScalarOp): ...@@ -1666,15 +1867,18 @@ class Exp(UnaryScalarOp):
return gz * exp(x), return gz * exp(x),
else: else:
return None, return None,
def c_code(self, node, name, (x, ), (z, ), sub): def c_code(self, node, name, (x, ), (z, ), sub):
if node.inputs[0].type in complex_types: if node.inputs[0].type in complex_types:
raise NotImplementedError('type not supported', type) raise NotImplementedError('type not supported', type)
return "%(z)s = exp(%(x)s);" % locals() return "%(z)s = exp(%(x)s);" % locals()
exp = Exp(upgrade_to_float, name = 'exp') exp = Exp(upgrade_to_float, name='exp')
class Sqr(UnaryScalarOp): class Sqr(UnaryScalarOp):
def impl(self, x): def impl(self, x):
return x*x return x * x
def grad(self, (x, ), (gz, )): def grad(self, (x, ), (gz, )):
if gz.type in complex_types: if gz.type in complex_types:
raise NotImplementedError() raise NotImplementedError()
...@@ -1685,27 +1889,32 @@ class Sqr(UnaryScalarOp): ...@@ -1685,27 +1889,32 @@ class Sqr(UnaryScalarOp):
def c_code(self, node, name, (x, ), (z, ), sub): def c_code(self, node, name, (x, ), (z, ), sub):
return "%(z)s = %(x)s * %(x)s;" % locals() return "%(z)s = %(x)s * %(x)s;" % locals()
sqr = Sqr(same_out, name = 'sqr') sqr = Sqr(same_out, name='sqr')
class Sqrt(UnaryScalarOp): class Sqrt(UnaryScalarOp):
def impl(self, x): def impl(self, x):
return numpy.sqrt(x) return numpy.sqrt(x)
def grad(self, (x, ), (gz, )):
def grad(self, (x,), (gz,)):
if gz.type in complex_types: if gz.type in complex_types:
raise NotImplementedError() raise NotImplementedError()
if x.type in float_types: if x.type in float_types:
return (gz * 0.5) / sqrt(x), return (gz * 0.5) / sqrt(x),
else: else:
return None, return None,
def c_code(self, node, name, (x, ), (z, ), sub):
def c_code(self, node, name, (x,), (z,), sub):
if node.inputs[0].type in complex_types: if node.inputs[0].type in complex_types:
raise NotImplementedError('type not supported', type) raise NotImplementedError('type not supported', type)
return "%(z)s = sqrt(%(x)s);" % locals() return "%(z)s = sqrt(%(x)s);" % locals()
sqrt = Sqrt(upgrade_to_float, name = 'sqrt') sqrt = Sqrt(upgrade_to_float, name='sqrt')
class Cos(UnaryScalarOp): class Cos(UnaryScalarOp):
def impl(self, x): def impl(self, x):
return numpy.cos(x) return numpy.cos(x)
def grad(self, (x, ), (gz, )): def grad(self, (x, ), (gz, )):
if gz.type in complex_types: if gz.type in complex_types:
raise NotImplementedError() raise NotImplementedError()
...@@ -1713,31 +1922,37 @@ class Cos(UnaryScalarOp): ...@@ -1713,31 +1922,37 @@ class Cos(UnaryScalarOp):
return -gz * sin(x), return -gz * sin(x),
else: else:
return None, return None,
def c_code(self, node, name, (x, ), (z, ), sub): def c_code(self, node, name, (x, ), (z, ), sub):
if node.inputs[0].type in complex_types: if node.inputs[0].type in complex_types:
raise NotImplementedError('type not supported', type) raise NotImplementedError('type not supported', type)
return "%(z)s = cos(%(x)s);" % locals() return "%(z)s = cos(%(x)s);" % locals()
cos = Cos(upgrade_to_float, name = 'cos') cos = Cos(upgrade_to_float, name='cos')
class Arccos(UnaryScalarOp): class Arccos(UnaryScalarOp):
def impl(self, x): def impl(self, x):
return numpy.arccos(x) return numpy.arccos(x)
def grad(self, (x, ), (gz, )):
def grad(self, (x,), (gz,)):
if gz.type in complex_types: if gz.type in complex_types:
raise NotImplementedError() raise NotImplementedError()
if x.type in float_types: if x.type in float_types:
return - gz / sqrt(numpy.cast[x.type](1) - sqr(x)), return - gz / sqrt(numpy.cast[x.type](1) - sqr(x)),
else: else:
return None, return None,
def c_code(self, node, name, (x, ), (z, ), sub):
def c_code(self, node, name, (x,), (z,), sub):
if node.inputs[0].type in complex_types: if node.inputs[0].type in complex_types:
raise NotImplementedError('type not supported', type) raise NotImplementedError('type not supported', type)
return "%(z)s = acos(%(x)s);" % locals() return "%(z)s = acos(%(x)s);" % locals()
arccos = Arccos(upgrade_to_float, name = 'arccos') arccos = Arccos(upgrade_to_float, name='arccos')
class Sin(UnaryScalarOp): class Sin(UnaryScalarOp):
def impl(self, x): def impl(self, x):
return numpy.sin(x) return numpy.sin(x)
def grad(self, (x, ), (gz, )): def grad(self, (x, ), (gz, )):
if x.type in complex_types: if x.type in complex_types:
raise NotImplementedError() raise NotImplementedError()
...@@ -1745,27 +1960,32 @@ class Sin(UnaryScalarOp): ...@@ -1745,27 +1960,32 @@ class Sin(UnaryScalarOp):
return gz * cos(x), return gz * cos(x),
else: else:
return None, return None,
def c_code(self, node, name, (x, ), (z, ), sub): def c_code(self, node, name, (x, ), (z, ), sub):
if node.inputs[0].type in complex_types: if node.inputs[0].type in complex_types:
raise NotImplementedError('type not supported', type) raise NotImplementedError('type not supported', type)
return "%(z)s = sin(%(x)s);" % locals() return "%(z)s = sin(%(x)s);" % locals()
sin = Sin(upgrade_to_float, name = 'sin') sin = Sin(upgrade_to_float, name='sin')
class Tan(UnaryScalarOp): class Tan(UnaryScalarOp):
def impl(self, x): def impl(self, x):
return numpy.tan(x) return numpy.tan(x)
def grad(self, (x, ), (gz, )):
def grad(self, (x,), (gz,)):
if x.type in complex_types: if x.type in complex_types:
raise NotImplementedError() raise NotImplementedError()
if x.type in float_types: if x.type in float_types:
return gz / sqr(cos(x)), return gz / sqr(cos(x)),
else: else:
return None, return None,
def c_code(self, node, name, (x, ), (z, ), sub): def c_code(self, node, name, (x, ), (z, ), sub):
if node.inputs[0].type in complex_types: if node.inputs[0].type in complex_types:
raise NotImplementedError('type not supported', type) raise NotImplementedError('type not supported', type)
return "%(z)s = tan(%(x)s);" % locals() return "%(z)s = tan(%(x)s);" % locals()
tan = Tan(upgrade_to_float, name = 'tan') tan = Tan(upgrade_to_float, name='tan')
class Cosh(UnaryScalarOp): class Cosh(UnaryScalarOp):
""" """
...@@ -1773,6 +1993,7 @@ class Cosh(UnaryScalarOp): ...@@ -1773,6 +1993,7 @@ class Cosh(UnaryScalarOp):
""" """
def impl(self, x): def impl(self, x):
return numpy.cosh(x) return numpy.cosh(x)
def grad(self, (x, ), (gz, )): def grad(self, (x, ), (gz, )):
if x.type in complex_types: if x.type in complex_types:
raise NotImplementedError() raise NotImplementedError()
...@@ -1780,11 +2001,13 @@ class Cosh(UnaryScalarOp): ...@@ -1780,11 +2001,13 @@ class Cosh(UnaryScalarOp):
return gz * sinh(x), return gz * sinh(x),
else: else:
return None, return None,
def c_code(self, node, name, (x, ), (z, ), sub): def c_code(self, node, name, (x, ), (z, ), sub):
if node.inputs[0].type in complex_types: if node.inputs[0].type in complex_types:
raise NotImplementedError('type not supported', type) raise NotImplementedError('type not supported', type)
return "%(z)s = cosh(%(x)s);" % locals() return "%(z)s = cosh(%(x)s);" % locals()
cosh = Cosh(upgrade_to_float, name = 'cosh') cosh = Cosh(upgrade_to_float, name='cosh')
class Sinh(UnaryScalarOp): class Sinh(UnaryScalarOp):
""" """
...@@ -1792,6 +2015,7 @@ class Sinh(UnaryScalarOp): ...@@ -1792,6 +2015,7 @@ class Sinh(UnaryScalarOp):
""" """
def impl(self, x): def impl(self, x):
return numpy.sinh(x) return numpy.sinh(x)
def grad(self, (x, ), (gz, )): def grad(self, (x, ), (gz, )):
if x.type in complex_types: if x.type in complex_types:
raise NotImplementedError() raise NotImplementedError()
...@@ -1799,11 +2023,13 @@ class Sinh(UnaryScalarOp): ...@@ -1799,11 +2023,13 @@ class Sinh(UnaryScalarOp):
return gz * cosh(x), return gz * cosh(x),
else: else:
return None, return None,
def c_code(self, node, name, (x, ), (z, ), sub): def c_code(self, node, name, (x, ), (z, ), sub):
if node.inputs[0].type in complex_types: if node.inputs[0].type in complex_types:
raise NotImplementedError('type not supported', type) raise NotImplementedError('type not supported', type)
return "%(z)s = sinh(%(x)s);" % locals() return "%(z)s = sinh(%(x)s);" % locals()
sinh = Sinh(upgrade_to_float, name = 'sinh') sinh = Sinh(upgrade_to_float, name='sinh')
class Tanh(UnaryScalarOp): class Tanh(UnaryScalarOp):
""" """
...@@ -1812,6 +2038,7 @@ class Tanh(UnaryScalarOp): ...@@ -1812,6 +2038,7 @@ class Tanh(UnaryScalarOp):
""" """
def impl(self, x): def impl(self, x):
return numpy.tanh(x) return numpy.tanh(x)
def grad(self, (x, ), (gz, )): def grad(self, (x, ), (gz, )):
if x.type in complex_types: if x.type in complex_types:
raise NotImplementedError() raise NotImplementedError()
...@@ -1819,36 +2046,43 @@ class Tanh(UnaryScalarOp): ...@@ -1819,36 +2046,43 @@ class Tanh(UnaryScalarOp):
return gz * (1 - sqr(tanh(x))), return gz * (1 - sqr(tanh(x))),
else: else:
return None, return None,
def c_code(self, node, name, (x, ), (z, ), sub): def c_code(self, node, name, (x, ), (z, ), sub):
if node.inputs[0].type in complex_types: if node.inputs[0].type in complex_types:
raise NotImplementedError('type not supported', type) raise NotImplementedError('type not supported', type)
return "%(z)s = tanh(%(x)s);" % locals() return "%(z)s = tanh(%(x)s);" % locals()
tanh = Tanh(upgrade_to_float, name = 'tanh') tanh = Tanh(upgrade_to_float, name='tanh')
class Real(UnaryScalarOp): class Real(UnaryScalarOp):
"""Extract the real coordinate of a complex number. """ """Extract the real coordinate of a complex number. """
def impl(self, x): def impl(self, x):
return numpy.real(x) return numpy.real(x)
def grad(self, (x, ), (gz, )): def grad(self, (x, ), (gz, )):
return [complex(gz, 0)] return [complex(gz, 0)]
real = Real(real_out, name='real') real = Real(real_out, name='real')
class Imag(UnaryScalarOp): class Imag(UnaryScalarOp):
def impl(self, x): def impl(self, x):
return numpy.imag(x) return numpy.imag(x)
def grad(self, (x, ), (gz, )): def grad(self, (x, ), (gz, )):
if x.type in complex_types: if x.type in complex_types:
return [complex(0, gz)] return [complex(0, gz)]
elif x.type in float_types: elif x.type in float_types:
return [second(x,0)] return [second(x, 0)]
else: else:
return [None] return [None]
imag = Imag(real_out, name='imag') imag = Imag(real_out, name='imag')
class Angle(UnaryScalarOp): class Angle(UnaryScalarOp):
def impl(self, x): def impl(self, x):
return numpy.angle(x) return numpy.angle(x)
def grad(self, (c, ), (gtheta, )): def grad(self, (c, ), (gtheta, )):
# y = x.imag # y = x.imag
# r = sqrt(y**2 + x.real**2) # r = sqrt(y**2 + x.real**2)
...@@ -1864,21 +2098,22 @@ class Angle(UnaryScalarOp): ...@@ -1864,21 +2098,22 @@ class Angle(UnaryScalarOp):
y = imag(c) y = imag(c)
r = abs(c) r = abs(c)
gr = -gtheta * y / (r**2 * sqrt(1 - (y/r)**2)) gr = -gtheta * y / (r ** 2 * sqrt(1 - (y / r) ** 2))
gx = gr * x/r gx = gr * x / r
gy = gr * y/r gy = gr * y / r
if c in complex_types: if c in complex_types:
return [cast(complex(gx, gy), x.type.dtype)] return [cast(complex(gx, gy), x.type.dtype)]
elif c in float_types: elif c in float_types:
return [cast(second(x,0), x.type.dtype)] return [cast(second(x, 0), x.type.dtype)]
else: else:
return [None] return [None]
angle = Angle(specific_out(float64), name='angle') angle = Angle(specific_out(float64), name='angle')
class Complex(BinaryScalarOp): class Complex(BinaryScalarOp):
@staticmethod @staticmethod
def output_types_preference(x,y): def output_types_preference(x, y):
if x in complex_types: if x in complex_types:
raise TypeError(x) raise TypeError(x)
if y in complex_types: if y in complex_types:
...@@ -1889,34 +2124,41 @@ class Complex(BinaryScalarOp): ...@@ -1889,34 +2124,41 @@ class Complex(BinaryScalarOp):
return [complex128] return [complex128]
else: else:
return [complex64] return [complex64]
def impl(self, x, y): def impl(self, x, y):
return numpy.complex(x, y) return numpy.complex(x, y)
def grad(self, (x,y), (gz,)):
def grad(self, (x, y), (gz,)):
return [cast(real(gz), x.type.dtype), return [cast(real(gz), x.type.dtype),
cast(imag(gz), y.type.dtype)] cast(imag(gz), y.type.dtype)]
complex = Complex(name='complex') complex = Complex(name='complex')
class Conj(UnaryScalarOp): class Conj(UnaryScalarOp):
def impl(self, x): def impl(self, x):
return numpy.conj(x) return numpy.conj(x)
def grad(self, (x, ), (gz, )): def grad(self, (x, ), (gz, )):
return [conj(gz)] return [conj(gz)]
conj = Conj(same_out, name='conj') conj = Conj(same_out, name='conj')
class ComplexFromPolar(BinaryScalarOp): class ComplexFromPolar(BinaryScalarOp):
@staticmethod @staticmethod
def output_types_preference(x,y): def output_types_preference(x, y):
return Complex.output_types_preference(x,y) return Complex.output_types_preference(x, y)
def impl(self, r, theta): def impl(self, r, theta):
if r < 0: if r < 0:
raise ValueError('polar radius must be non-negative', r) raise ValueError('polar radius must be non-negative', r)
x = r*numpy.cos(theta) x = r * numpy.cos(theta)
y = r*numpy.sin(theta) y = r * numpy.sin(theta)
if x.dtype == 'float32': if x.dtype == 'float32':
return numpy.complex64(numpy.complex(x,y)) return numpy.complex64(numpy.complex(x, y))
else: else:
return numpy.complex128(numpy.complex(x,y)) return numpy.complex128(numpy.complex(x, y))
def grad(self, (r,theta), (gz,)):
def grad(self, (r, theta), (gz,)):
gr = cos(theta) * real(gz) + sin(theta) * imag(gz) gr = cos(theta) * real(gz) + sin(theta) * imag(gz)
gtheta = -real(gz) * r * sin(theta) + imag(gz) * r * cos(theta) gtheta = -real(gz) * r * sin(theta) + imag(gz) * r * cos(theta)
return [cast(gr, r.type.dtype), return [cast(gr, r.type.dtype),
...@@ -1935,29 +2177,29 @@ class Composite(ScalarOp): ...@@ -1935,29 +2177,29 @@ class Composite(ScalarOp):
def __str__(self): def __str__(self):
return self.name return self.name
def make_new_inplace(self, output_types_preference = None, name = None): def make_new_inplace(self, output_types_preference=None, name=None):
""" """
This op.__init__ fct don't have the same parameter as other scalar op. This op.__init__ fct don't have the same parameter as other scalar op.
This break the insert_inplace_optimizer optimization. This break the insert_inplace_optimizer optimization.
This fct allow fix patch this. This fct allow fix patch this.
""" """
out = self.__class__(self.inputs,self.outputs) out = self.__class__(self.inputs, self.outputs)
if name: if name:
out.name = name out.name = name
else: else:
name = out.name name = out.name
super(Composite,out).__init__(output_types_preference, name) super(Composite, out).__init__(output_types_preference, name)
return out return out
def init_c_code(self): def init_c_code(self):
"""Return the C code for this Composite Op. """ """Return the C code for this Composite Op. """
subd = dict( subd = dict(
zip(self.env.inputs, zip(self.env.inputs,
["%%(i%i)s"%i for i in xrange(len(self.env.inputs))]) ["%%(i%i)s" % i for i in xrange(len(self.env.inputs))])
+ zip(self.env.outputs, + zip(self.env.outputs,
["%%(o%i)s"%i for i in xrange(len(self.env.outputs))])) ["%%(o%i)s" % i for i in xrange(len(self.env.outputs))]))
for orphan in self.env.variables: #env.orphans: for orphan in self.env.variables: # env.orphans:
if orphan.owner is None and orphan not in self.env.inputs: if orphan.owner is None and orphan not in self.env.inputs:
if isinstance(orphan, Constant): if isinstance(orphan, Constant):
subd[orphan] = orphan.type.c_literal(orphan.data) subd[orphan] = orphan.type.c_literal(orphan.data)
...@@ -1984,8 +2226,8 @@ class Composite(ScalarOp): ...@@ -1984,8 +2226,8 @@ class Composite(ScalarOp):
self.nodenames[j], self.nodenames[j],
[subd[input] for input in node.inputs], [subd[input] for input in node.inputs],
[subd[output] for output in node.outputs], [subd[output] for output in node.outputs],
dict(fail = "%(fail)s", dict(fail="%(fail)s",
id = "%%(id)s_%i" % j)) id="%%(id)s_%i" % j))
_c_code += s _c_code += s
_c_code += "\n" _c_code += "\n"
_c_code += "}\n" _c_code += "}\n"
...@@ -2002,7 +2244,7 @@ class Composite(ScalarOp): ...@@ -2002,7 +2244,7 @@ class Composite(ScalarOp):
if r in self.env.inputs: if r in self.env.inputs:
idx = self.env.inputs.index(r) idx = self.env.inputs.index(r)
return lambda inputs: inputs[idx] return lambda inputs: inputs[idx]
elif r.owner is None: # in env.orphans: elif r.owner is None: # in env.orphans:
return lambda inputs: r.data return lambda inputs: r.data
node = r.owner node = r.owner
producers = [compose_impl(input) for input in node.inputs] producers = [compose_impl(input) for input in node.inputs]
...@@ -2016,25 +2258,25 @@ class Composite(ScalarOp): ...@@ -2016,25 +2258,25 @@ class Composite(ScalarOp):
rval = self.name rval = self.name
except AttributeError: except AttributeError:
if 0: if 0:
l=[] l = []
for n in env.toposort(): for n in env.toposort():
if hasattr(n.op,"name") and n.op.name is not None: if hasattr(n.op, "name") and n.op.name is not None:
v=n.op.name v = n.op.name
if v.startswith("Composite"): if v.startswith("Composite"):
v = v[len("Composite"):] v = v[len("Composite"):]
else: else:
v=n.op.__class__.__name__ v = n.op.__class__.__name__
l.append(v) l.append(v)
rval = "Composite{"+",".join(l)+"}" rval = "Composite{" + ",".join(l) + "}"
else: else:
for i, r in enumerate(self.env.inputs): for i, r in enumerate(self.env.inputs):
r.name='i%i' % i r.name = 'i%i' % i
for i, r in enumerate(self.env.outputs): for i, r in enumerate(self.env.outputs):
r.name='o%i' % i r.name = 'o%i' % i
io = set(self.env.inputs + self.env.outputs) io = set(self.env.inputs + self.env.outputs)
for i, r in enumerate(self.env.variables): for i, r in enumerate(self.env.variables):
if r not in io and len(r.clients) > 1: if r not in io and len(r.clients) > 1:
r.name='t%i' % i r.name = 't%i' % i
rval = "Composite{%s}" % str(self.env) rval = "Composite{%s}" % str(self.env)
self.name = rval self.name = rval
...@@ -2043,12 +2285,13 @@ class Composite(ScalarOp): ...@@ -2043,12 +2285,13 @@ class Composite(ScalarOp):
gof.MergeOptimizer().optimize(env) gof.MergeOptimizer().optimize(env)
for node in env.nodes: for node in env.nodes:
if not isinstance(node.op, ScalarOp): if not isinstance(node.op, ScalarOp):
raise ValueError("The env to Composite must be exclusively composed of ScalarOp instances.") raise ValueError("The env to Composite must be exclusively"
" composed of ScalarOp instances.")
self.env = env self.env = env
def __init__(self, inputs, outputs): def __init__(self, inputs, outputs):
self.inputs=copy(inputs) self.inputs = copy(inputs)
self.outputs=copy(outputs) self.outputs = copy(outputs)
self.inputs_type = tuple([input.type for input in inputs]) self.inputs_type = tuple([input.type for input in inputs])
self.outputs_type = tuple([output.type for output in outputs]) self.outputs_type = tuple([output.type for output in outputs])
self.nin = len(inputs) self.nin = len(inputs)
...@@ -2071,22 +2314,23 @@ class Composite(ScalarOp): ...@@ -2071,22 +2314,23 @@ class Composite(ScalarOp):
def impl(self, *inputs): def impl(self, *inputs):
output_storage = [[None] for i in xrange(self.nout)] output_storage = [[None] for i in xrange(self.nout)]
self.perform(None, inputs, output_storage) self.perform(None, inputs, output_storage)
return utils.to_return_values([storage[0] for storage in output_storage]) return utils.to_return_values([storage[0] for storage in
output_storage])
def grad(self, inputs, output_grads): def grad(self, inputs, output_grads):
raise NotImplementedError("grad is not implemented for Composite") raise NotImplementedError("grad is not implemented for Composite")
def c_code(self, node, nodename, inames, onames, sub): def c_code(self, node, nodename, inames, onames, sub):
d = dict(zip(["i%i"%i for i in xrange(len(inames))], d = dict(zip(["i%i" % i for i in xrange(len(inames))],
inames) + inames) +
zip(["o%i"%i for i in xrange(len(onames))], zip(["o%i" % i for i in xrange(len(onames))],
onames), onames),
**sub) **sub)
d['nodename'] = nodename d['nodename'] = nodename
if not sub.has_key('id'): if not sub.has_key('id'):
#The use of a dummy id is safe as the code is in a separate block. #The use of a dummy id is safe as the code is in a separate block.
#It won't generate conflicting variable name. #It won't generate conflicting variable name.
d['id']='_DUMMY_ID_' d['id'] = '_DUMMY_ID_'
return self._c_code % d return self._c_code % d
......
...@@ -4175,10 +4175,6 @@ class Join(Op): ...@@ -4175,10 +4175,6 @@ class Join(Op):
outputs = [output_maker(bcastable)] outputs = [output_maker(bcastable)]
node = Apply(self, inputs, outputs) node = Apply(self, inputs, outputs)
if python_any(not x.type.broadcastable[0] for x in orig):
node.tag.shape_zero = None
else:
node.tag.shape_zero = len(orig)
return node return node
def perform(self, node, axis_and_tensors, out_): def perform(self, node, axis_and_tensors, out_):
...@@ -4225,17 +4221,6 @@ class Join(Op): ...@@ -4225,17 +4221,6 @@ class Join(Op):
[slice(None)] * (n_dims - axis - 1)] \ [slice(None)] * (n_dims - axis - 1)] \
for k in xrange(len(sizes_along_axis))] for k in xrange(len(sizes_along_axis))]
def vec_length(self, node):
"""Guess the length of a Join Variable"""
assert isinstance(node.owner.op, Join)
if node.ndim != 1:
raise TypeError('argument must be symbolic vector')
if node.owner.tag.shape_zero is None:
raise ValueError("could not determine vector length")
else:
return node.owner.tag.shape_zero
def infer_shape(self, node, ishapes): def infer_shape(self, node, ishapes):
# ishapes[0] contains the size of the axis on which we join # ishapes[0] contains the size of the axis on which we join
# Join op should get at least one input to join # Join op should get at least one input to join
...@@ -4427,11 +4412,6 @@ def get_vector_length(v): ...@@ -4427,11 +4412,6 @@ def get_vector_length(v):
return 1 return 1
if isinstance(v, gof.Constant) and v.type.ndim == 1: if isinstance(v, gof.Constant) and v.type.ndim == 1:
return len(v.data) return len(v.data)
if v.owner and isinstance(v.owner.op, Join):
try:
return join.vec_length(v)
except ValueError:
pass
if v.owner and isinstance(v.owner.op, theano.tensor.opt.MakeVector): if v.owner and isinstance(v.owner.op, theano.tensor.opt.MakeVector):
return len(v.owner.inputs) return len(v.owner.inputs)
if v.owner and isinstance(v.owner.op, Shape): if v.owner and isinstance(v.owner.op, Shape):
......
...@@ -259,9 +259,8 @@ def grad(cost, wrt, g_cost=None, consider_constant=None, warn_type=False, ...@@ -259,9 +259,8 @@ def grad(cost, wrt, g_cost=None, consider_constant=None, warn_type=False,
:return: symbolic expression of gradient of `cost` with respect to `wrt`. :return: symbolic expression of gradient of `cost` with respect to `wrt`.
If an element of `wrt` is not differentiable with respect If an element of `wrt` is not differentiable with respect
to the output, then a zero variable is returned. to the output, then a zero variable is returned.
If `wrt` is a list/tuple, longer then 1, a list will be returned. It returns an object of same type as `wrt`: a list/tuple
DEPRECATION: In Theano 0.5, grad will return an object of the same or TensorVariable in all cases.
type as `wrt`: a list/tuple or TensorVariable in all case.
This function is a wrapper around the more general function This function is a wrapper around the more general function
`theano.gradient.grad_sources_inputs``. `theano.gradient.grad_sources_inputs``.
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论