提交 7dafb259 authored 作者: Olivier Delalleau's avatar Olivier Delalleau

Giving up on trying to mimic numpy's mixed scalar/array behavior

上级 5e27dfd9
......@@ -158,19 +158,25 @@ import theano and print the config variable, as in:
Default: 'custom'
This specifies how data types are implicitly figured out in Theano, e.g. for
constants or in the result of arithmetic operations. The recommended value is
'numpy+floatX', that mimics numpy's behavior except for floats when
``config.floatX`` is set to 'float32', for which we use float32 instead of
float64 unless the user is explicitly using data typed as float64. When
'numpy' is used, this specific floatX behavior is discarded. The current
default value is 'custom' for backward compatibility reason, and corresponds
to a set of custom rules originally used in Theano (which can be partially
customized, see e.g. the in-code help of ``tensor.NumpyAutocaster``). The
'custom' option will be deprecated in a future release of Theano.
**Until further notice, it is strongly advised to never change this option
within a script, and to always clean your Theano cache whenever you modify its
value**.
This specifies how data types are implicitly figured out in Theano, e.g. for
constants or in the results of arithmetic operations. The current default
value ('custom') corresponds to a set of custom rules originally used in
Theano (which can be partially customized, see e.g. the in-code help of
``tensor.NumpyAutocaster``). However the 'custom' option will be
deprecated in a future release of Theano. The 'numpy' setting attempts to
mimic the numpy casting rules. 'numpy+floatX' does the same, except that
it prefers to use float32 numbers instead of float64 when ``config.floatX``
is set to 'float32' (this will become the default value in a future
release of Theano). Note that both 'numpy' and 'numpy+floatX'
behave differently from numpy on purpose in the following situations:
* Depending on the value of ``config.int_division``, the resulting type
of a division of integer types with the ``/`` operator may not match
that of numpy.
* On mixed scalar / array operations, numpy tries to prevent the scalar
from upcasting the array's type unless it is of a fundamentally
different type. However it is not practical to implement in Theano
a behavior similar to the one currently found in numpy, so Theano
does not attempt to do the same.
.. attribute:: int_division
......@@ -178,14 +184,14 @@ import theano and print the config variable, as in:
Default: 'int'
Specifies what to do when one tries to compute `x / y`, where both `x` and
`y` are of integer types (possibly unsigned). 'int' means an integer is
Specifies what to do when one tries to compute ``x / y``, where both ``x`` and
``y`` are of integer types (possibly unsigned). 'int' means an integer is
returned (as in Python 2.X), but this behavior is deprecated. 'floatX'
returns a number of type given by ``config.floatX`. 'raise' is the safest
returns a number of type given by ``config.floatX``. 'raise' is the safest
choice (and will become default in a future release of Theano) and raises
an error when one tries to do such an operation, enforcing the use of the
integer division operator (``//``) (if a float result is intended, either
cast one of the arguments to a float, or use `x.__truediv__(y)`).
cast one of the arguments to a float, or use ``x.__truediv__(y)``).
.. attribute:: mode
......
......@@ -4407,7 +4407,7 @@ def _test_autocast_numpy_floatX():
class test_arithmetic_cast(unittest.TestCase):
"""
Test output types of typical arithmeric operations (* / + - //).
Test output types of basic arithmeric operations (* / + - //).
We only test the behavior for `config.cast_policy` set to either 'numpy' or
'numpy+floatX': the 'custom' behavior is (at least partially) tested in
......@@ -4435,11 +4435,13 @@ class test_arithmetic_cast(unittest.TestCase):
for a_type in dtypes:
for b_type in dtypes:
# Note that we do not test division between
# integers as this is currently forbidden.
if (op is operator.div and
a_type in tensor.discrete_dtypes and
b_type in tensor.discrete_dtypes):
continue
# integers if it is forbidden.
# Theano deals with integer division in its own
# special way (depending on `config.int_division`).
is_int_division = (
op is operator.div and
a_type in tensor.discrete_dtypes and
b_type in tensor.discrete_dtypes)
# We will test all meaningful combinations of
# scalar and array operations.
for combo in (
......@@ -4449,17 +4451,29 @@ class test_arithmetic_cast(unittest.TestCase):
('array', 'scalar'),
('i_scalar', 'i_scalar'),
):
theano_args = map(eval,
['theano_%s' % c for c in combo])
numpy_args = map(eval,
['numpy_%s' % c for c in combo])
theano_dtype = op(
try:
theano_dtype = op(
theano_args[0](a_type),
theano_args[1](b_type)).type.dtype
# Should have crashed if it is an integer
# division and `config.int_division` does
# not allow it.
assert not (is_int_division and
config.int_division == 'raise')
except theano.scalar.IntegerDivisionError:
assert (is_int_division and
config.int_division == 'raise')
# This is the expected behavior.
continue
# For numpy we have a problem:
# http://projects.scipy.org/numpy/ticket/1827
# The current expected behavior is to use
# the highest data type that numpy may return.
# As a result we only consider the highest data
# type that numpy may return.
numpy_dtypes = [
op(numpy_args[0](a_type),
numpy_args[1](b_type)).dtype,
......@@ -4467,6 +4481,9 @@ class test_arithmetic_cast(unittest.TestCase):
numpy_args[0](a_type)).dtype]
numpy_dtype = theano.scalar.upcast(
*map(str, numpy_dtypes))
if numpy_dtype == theano_dtype:
# Same data type found, all is good!
continue
if (cfg == 'numpy+floatX' and
config.floatX == 'float32' and
a_type != 'float64' and
......@@ -4474,8 +4491,40 @@ class test_arithmetic_cast(unittest.TestCase):
numpy_dtype == 'float64'):
# We should keep float32.
assert theano_dtype == 'float32'
else:
assert theano_dtype == numpy_dtype
continue
if 'array' in combo and 'scalar' in combo:
# For mixed scalar / array operations,
# Theano may differ from numpy as it does
# not try to prevent the scalar from
# upcasting the array.
array_type, scalar_type = (
(a_type, b_type)[
list(combo).index(arg)]
for arg in ('array', 'scalar'))
up_type = theano.scalar.upcast(array_type,
scalar_type)
if (
# The two data types are different.
scalar_type != array_type and
# The array type is not enough to hold
# the scalar type as well.
array_type != up_type and
# Theano upcasted the result array.
theano_dtype == up_type and
# But Numpy kept its original type.
# (not an equality because of numpy bug
# mentioned above).
array_type in numpy_dtypes):
# Then we accept this difference in
# behavior.
continue
if (is_int_division and
config.int_division == 'floatX'):
assert theano_dtype == config.floatX
continue
# In any other situation: something wrong is
# going on!
assert False
finally:
config.cast_policy = backup_config
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论