def convert(tensor, src_axes, dst_axes):
        """
        Returns a view of tensor using the axis semantics defined
        by dst_axes. (If src_axes matches dst_axes, returns
        tensor itself)
        Useful for transferring tensors between different
        Conv2DSpaces.
        Parameters
        ----------
        tensor : tensor_like
            A 4-tensor representing a batch of images
        src_axes : WRITEME
            Axis semantics of tensor
        dst_axes : WRITEME
            WRITEME
        """
        src_axes = tuple(src_axes)
        dst_axes = tuple(dst_axes)
        assert len(src_axes) == 5
        assert len(dst_axes) == 5

        if src_axes == dst_axes:
            return tensor

        shuffle = [src_axes.index(elem) for elem in dst_axes]

        if is_symbolic_batch(tensor):
            return tensor.dimshuffle(*shuffle)
        else:
            return tensor.transpose(*shuffle)
Example #2
0
    def convert(tensor, src_axes, dst_axes):
        """
        Returns a view of tensor using the axis semantics defined
        by dst_axes. (If src_axes matches dst_axes, returns
        tensor itself)
        Useful for transferring tensors between different
        Conv2DSpaces.
        Parameters
        ----------
        tensor : tensor_like
            A 4-tensor representing a batch of images
        src_axes : WRITEME
            Axis semantics of tensor
        dst_axes : WRITEME
            WRITEME
        """
        src_axes = tuple(src_axes)
        dst_axes = tuple(dst_axes)
        assert len(src_axes) == 5
        assert len(dst_axes) == 5

        if src_axes == dst_axes:
            return tensor

        shuffle = [src_axes.index(elem) for elem in dst_axes]

        if is_symbolic_batch(tensor):
            return tensor.dimshuffle(*shuffle)
        else:
            return tensor.transpose(*shuffle)
Example #3
0
        def get_expected_error(from_space, from_batch, to_space):
            """
            Returns the type of error to be expected when calling
            from_space.np_format_as(batch, to_space). Returns None if no error
            should be expected.
            """

            def contains_different_dtypes(space):
                """
                Returns true if space contains different dtypes. None is
                considered distinct from all actual dtypes.
                """

                assert isinstance(space, CompositeSpace)

                def get_shared_dtype_if_any(space):
                    """
                    Returns space's dtype. If space is composite, returns the
                    dtype used by all of its subcomponents. Returns False if
                    the subcomponents use different dtypes.
                    """
                    if isinstance(space, CompositeSpace):
                        dtypes = tuple(get_shared_dtype_if_any(c)
                                       for c in space.components)
                        assert(len(dtypes) > 0)
                        if any(d != dtypes[0] for d in dtypes[1:]):
                            return False

                        return dtypes[0]  # could be False, but that's fine
                    else:
                        return space.dtype

                return get_shared_dtype_if_any(space) is False

            assert (isinstance(from_space, CompositeSpace) ==
                    isinstance(from_batch, tuple))

            # composite -> composite
            if isinstance(from_space, CompositeSpace) and \
               isinstance(to_space, CompositeSpace):
                for fs, fb, ts in safe_zip(from_space.components,
                                           from_batch,
                                           to_space.components):
                    error, message = get_expected_error(fs, fb, ts)
                    if error is not None:
                        return error, message

                return None, None

            # composite -> simple
            if isinstance(from_space, CompositeSpace):
                if isinstance(to_space, Conv2DSpace):
                    return (NotImplementedError,
                            "CompositeSpace does not know how to format as "
                            "Conv2DSpace")

                for fs, fb in safe_zip(from_space.components, from_batch):
                    error, message = get_expected_error(fs, fb, to_space)
                    if error is not None:
                        return error, message

                if isinstance(to_space, VectorSpace) and \
                   contains_different_dtypes(from_space) and \
                   to_space.dtype is None:
                    return (TypeError,
                            "Tried to format components with differing dtypes "
                            "into a VectorSpace with no dtype of its own. "
                            "dtypes: ")

                return None, None

            # simple -> composite
            if isinstance(to_space, CompositeSpace):

                if isinstance(from_space, VectorSpace) and \
                   isinstance(from_batch, theano.sparse.SparseVariable):
                    assert from_space.sparse
                    return (UserWarning,
                            'Formatting from a sparse VectorSpace to a '
                            'CompositeSpace is currently (2 Jan 2014) a '
                            'non-differentiable action. This is because it '
                            'calls slicing operations on a sparse batch '
                            '(e.g. "my_matrix[r:R, c:C]", which Theano does '
                            'not yet have a gradient operator for. If '
                            'autodifferentiation is reporting an error, '
                            'this may be why.')

                if isinstance(from_space, Conv2DSpace):
                    return (NotImplementedError,
                            "Conv2DSpace does not know how to format as "
                            "CompositeSpace")

                for ts in to_space.components:
                    error, message = get_expected_error(from_space,
                                                        from_batch,
                                                        ts)
                    if error is not None:
                        return error, message

                return None, None

            #
            # simple -> simple
            #

            def is_sparse(space):
                return isinstance(space, VectorSpace) and space.sparse

            def is_complex(arg):
                """
                Returns whether a space or a batch has a complex dtype.
                """
                return (arg.dtype is not None and
                        str(arg.dtype).startswith('complex'))

            if isinstance(from_batch, tuple):
                return (TypeError,
                        "This space only supports simple dtypes, but received "
                        "a composite batch.")

            if is_complex(from_batch) and not is_complex(from_space):
                return (TypeError,
                        "This space has a non-complex dtype (%s), and "
                        "thus cannot support complex batches of type %s." %
                        (from_space.dtype, from_batch.dtype))

            if from_space.dtype is not None and \
               from_space.dtype != from_batch.dtype:
                return (TypeError,
                        "This space is for dtype %s, but recieved a "
                        "batch of dtype %s." %
                        (from_space.dtype, from_batch.dtype))

            if is_sparse(from_space) and isinstance(to_space, Conv2DSpace):
                return (TypeError,
                        "Formatting a SparseVariable to a Conv2DSpace "
                        "is not supported, since neither scipy nor "
                        "Theano has sparse tensors with more than 2 "
                        "dimensions. We need 4 dimensions to "
                        "represent a Conv2DSpace batch")

            if is_complex(from_space) and not is_complex(to_space):
                if is_symbolic_batch(from_batch):
                    return (TypeError,
                            "Casting from complex to real is ambiguous")
                else:
                    return (np.ComplexWarning,
                            "Casting complex values to real discards the "
                            "imaginary part")

            return None, None
Example #4
0
                                         from_batch,
                                         to_space)
        expected_dtypes = get_expected_formatted_dtype(from_batch, to_space)
        actual_dtypes = get_batch_dtype(to_batch)

        assert expected_dtypes == actual_dtypes, \
            ("\nexpected_dtypes: %s,\n"
             "actual_dtypes: %s \n"
             "from_space: %s\n"
             "from_batch's dtype: %s\n"
             "from_batch is theano?: %s\n"
             "to_space: %s" % (expected_dtypes,
                               actual_dtypes,
                               from_space,
                               get_batch_dtype(from_batch),
                               is_symbolic_batch(from_batch),
                               to_space))

    #
    #
    # End of test_format() function.

    def test_dtype_getter(space):
        """
        Tests the getter method of space's dtype property.
        """

        def assert_composite_dtype_eq(space, dtype):
            """
            Asserts that dtype is a nested tuple with exactly the same tree
            structure as space, and that the dtypes of space's components and
Example #5
0
        def get_expected_error(from_space, from_batch, to_space):
            """
            Returns the type of error to be expected when calling
            from_space.np_format_as(batch, to_space). Returns None if no error
            should be expected.
            """
            def contains_different_dtypes(space):
                """
                Returns true if space contains different dtypes. None is
                considered distinct from all actual dtypes.
                """

                assert isinstance(space, CompositeSpace)

                def get_shared_dtype_if_any(space):
                    """
                    Returns space's dtype. If space is composite, returns the
                    dtype used by all of its subcomponents. Returns False if
                    the subcomponents use different dtypes.
                    """
                    if isinstance(space, CompositeSpace):
                        dtypes = tuple(
                            get_shared_dtype_if_any(c)
                            for c in space.components)
                        assert (len(dtypes) > 0)
                        if any(d != dtypes[0] for d in dtypes[1:]):
                            return False

                        return dtypes[0]  # could be False, but that's fine
                    else:
                        return space.dtype

                return get_shared_dtype_if_any(space) is False

            assert (isinstance(from_space, CompositeSpace) == isinstance(
                from_batch, tuple))

            # composite -> composite
            if isinstance(from_space, CompositeSpace) and \
               isinstance(to_space, CompositeSpace):
                for fs, fb, ts in safe_zip(from_space.components, from_batch,
                                           to_space.components):
                    error, message = get_expected_error(fs, fb, ts)
                    if error is not None:
                        return error, message

                return None, None

            # composite -> simple
            if isinstance(from_space, CompositeSpace):
                if isinstance(to_space, Conv2DSpace):
                    return (NotImplementedError,
                            "CompositeSpace does not know how to format as "
                            "Conv2DSpace")

                for fs, fb in safe_zip(from_space.components, from_batch):
                    error, message = get_expected_error(fs, fb, to_space)
                    if error is not None:
                        return error, message

                if isinstance(to_space, VectorSpace) and \
                   contains_different_dtypes(from_space) and \
                   to_space.dtype is None:
                    return (TypeError,
                            "Tried to format components with differing dtypes "
                            "into a VectorSpace with no dtype of its own. "
                            "dtypes: ")

                return None, None

            # simple -> composite
            if isinstance(to_space, CompositeSpace):

                if isinstance(from_space, VectorSpace) and \
                   isinstance(from_batch, theano.sparse.SparseVariable):
                    assert from_space.sparse
                    return (UserWarning,
                            'Formatting from a sparse VectorSpace to a '
                            'CompositeSpace is currently (2 Jan 2014) a '
                            'non-differentiable action. This is because it '
                            'calls slicing operations on a sparse batch '
                            '(e.g. "my_matrix[r:R, c:C]", which Theano does '
                            'not yet have a gradient operator for. If '
                            'autodifferentiation is reporting an error, '
                            'this may be why.')

                if isinstance(from_space, Conv2DSpace):
                    return (NotImplementedError,
                            "Conv2DSpace does not know how to format as "
                            "CompositeSpace")

                for ts in to_space.components:
                    error, message = get_expected_error(
                        from_space, from_batch, ts)
                    if error is not None:
                        return error, message

                return None, None

            #
            # simple -> simple
            #

            def is_sparse(space):
                return isinstance(space, VectorSpace) and space.sparse

            def is_complex(arg):
                """
                Returns whether a space or a batch has a complex dtype.
                """
                return (arg.dtype is not None
                        and str(arg.dtype).startswith('complex'))

            if isinstance(from_batch, tuple):
                return (TypeError,
                        "This space only supports simple dtypes, but received "
                        "a composite batch.")

            if is_complex(from_batch) and not is_complex(from_space):
                return (TypeError,
                        "This space has a non-complex dtype (%s), and "
                        "thus cannot support complex batches of type %s." %
                        (from_space.dtype, from_batch.dtype))

            if from_space.dtype is not None and \
               from_space.dtype != from_batch.dtype:
                return (TypeError,
                        "This space is for dtype %s, but recieved a "
                        "batch of dtype %s." %
                        (from_space.dtype, from_batch.dtype))

            if is_sparse(from_space) and isinstance(to_space, Conv2DSpace):
                return (TypeError,
                        "Formatting a SparseVariable to a Conv2DSpace "
                        "is not supported, since neither scipy nor "
                        "Theano has sparse tensors with more than 2 "
                        "dimensions. We need 4 dimensions to "
                        "represent a Conv2DSpace batch")

            if is_complex(from_space) and not is_complex(to_space):
                if is_symbolic_batch(from_batch):
                    return (TypeError,
                            "Casting from complex to real is ambiguous")
                else:
                    return (np.ComplexWarning,
                            "Casting complex values to real discards the "
                            "imaginary part")

            return None, None
Example #6
0
        to_batch = from_space._format_as(using_numeric_batch, from_batch,
                                         to_space)
        expected_dtypes = get_expected_formatted_dtype(from_batch, to_space)
        actual_dtypes = get_batch_dtype(to_batch)

        assert expected_dtypes == actual_dtypes, \
               ("\nexpected_dtypes: %s,\n"
                "actual_dtypes: %s \n"
                "from_space: %s\n"
                "from_batch's dtype: %s\n"
                "from_batch is theano?: %s\n"
                "to_space: %s" % (expected_dtypes,
                                  actual_dtypes,
                                  from_space,
                                  get_batch_dtype(from_batch),
                                  is_symbolic_batch(from_batch),
                                  to_space))

    #
    #
    # End of test_format() function.

    def test_dtype_getter(space):
        """
        Tests the getter method of space's dtype property.
        """
        def assert_composite_dtype_eq(space, dtype):
            """
            Asserts that dtype is a nested tuple with exactly the same tree
            structure as space, and that the dtypes of space's components and
            their corresponding elements in <dtype> are equal.
Example #7
0
    def test_format(from_space, to_space, using_numeric_batch):
        """
        Unit test for a call to from_space.np_format_as(batch, to_space)
        """

        # Type-checks the arguments
        for space, name in zip((from_space, to_space),
                               ("from_space", "to_space")):
            if not isinstance(space,
                              (VectorSpace, Conv2DSpace, CompositeSpace)):
                raise TypeError("This test only supports spaces of type "
                                "VectorSpace, Conv2DSpace, and "
                                "CompositeSpace, not %s's type %s" %
                                (name, type(space)))

        def get_batch(space, using_numeric_batch):
            """
            Uses space.get_origin_batch() to return a numeric batch,
            or space.get_theano_batch() to return a symbolic
            Uses a fallback dtype if the space itself doesn't have one.
            """

            def specifies_all_dtypes(space):
                """
                Returns True iff space has a completely specified dtype.
                """
                if isinstance(space, CompositeSpace):
                    return all(specifies_all_dtypes(subspace)
                               for subspace in space.components)
                else:
                    return space.dtype is not None

            def replace_none_dtypes(dtype, fallback_dtype):
                """
                Returns dtype, with any Nones replaced by fallback_dtype.
                """

                if isinstance(dtype, tuple):
                    return tuple(replace_none_dtypes(d, fallback_dtype)
                                 for d in dtype)
                else:
                    return fallback_dtype if dtype is None else dtype

            kwargs = {"batch_size": batch_size}

            # Use this when space doesn't specify a dtype
            fallback_dtype = theano.config.floatX

            if not specifies_all_dtypes(space):
                kwargs["dtype"] = replace_none_dtypes(space.dtype,
                                                      fallback_dtype)

            if using_numeric_batch:
                return space.get_origin_batch(**kwargs)
            else:
                # Sparse VectorSpaces throw an exception if batch_size is
                # specified
                if isinstance(space, VectorSpace) and space.sparse:
                    del kwargs["batch_size"]

                kwargs["name"] = "space-generated batch"
                return space.make_theano_batch(**kwargs)

        def get_expected_warning(from_space, from_batch, to_space):

            # composite -> composite
            if isinstance(from_space, CompositeSpace) and \
               isinstance(to_space, CompositeSpace):
                for fs, fb, ts in safe_zip(from_space.components,
                                           from_batch,
                                           to_space.components):
                    warning, message = get_expected_warning(fs, fb, ts)
                    if warning is not None:
                        return warning, message

                return None, None

            # composite -> simple
            if isinstance(from_space, CompositeSpace):
                for fs, fb in safe_zip(from_space.components, from_batch):
                    warning, message = get_expected_warning(fs, fb, to_space)
                    if warning is not None:
                        return warning, message

                return None, None

            # simple -> composite
            if isinstance(to_space, CompositeSpace):
                if isinstance(from_space, VectorSpace) and \
                   isinstance(from_batch, theano.sparse.SparseVariable):
                    assert from_space.sparse
                    return (UserWarning,
                            'Formatting from a sparse VectorSpace to a '
                            'CompositeSpace is currently (2 Jan 2014) a '
                            'non-differentiable action. This is because it '
                            'calls slicing operations on a sparse batch '
                            '(e.g. "my_matrix[r:R, c:C]", which Theano does '
                            'not yet have a gradient operator for. If '
                            'autodifferentiation is reporting an error, '
                            'this may be why.')

                for ts in to_space.components:
                    warning, message = get_expected_warning(from_space,
                                                            from_batch,
                                                            ts)
                    if warning is not None:
                        return warning, message

                return None, None

            # simple -> simple
            return None, None

        def get_expected_error(from_space, from_batch, to_space):
            """
            Returns the type of error to be expected when calling
            from_space.np_format_as(batch, to_space). Returns None if no error
            should be expected.
            """

            def contains_different_dtypes(space):
                """
                Returns true if space contains different dtypes. None is
                considered distinct from all actual dtypes.
                """

                assert isinstance(space, CompositeSpace)

                def get_shared_dtype_if_any(space):
                    """
                    Returns space's dtype. If space is composite, returns the
                    dtype used by all of its subcomponents. Returns False if
                    the subcomponents use different dtypes.
                    """
                    if isinstance(space, CompositeSpace):
                        dtypes = tuple(get_shared_dtype_if_any(c)
                                       for c in space.components)
                        assert(len(dtypes) > 0)
                        if any(d != dtypes[0] for d in dtypes[1:]):
                            return False

                        return dtypes[0]  # could be False, but that's fine
                    else:
                        return space.dtype

                return get_shared_dtype_if_any(space) is False

            assert (isinstance(from_space, CompositeSpace) ==
                    isinstance(from_batch, tuple))

            # composite -> composite
            if isinstance(from_space, CompositeSpace) and \
               isinstance(to_space, CompositeSpace):
                for fs, fb, ts in safe_zip(from_space.components,
                                           from_batch,
                                           to_space.components):
                    error, message = get_expected_error(fs, fb, ts)
                    if error is not None:
                        return error, message

                return None, None

            # composite -> simple
            if isinstance(from_space, CompositeSpace):
                if isinstance(to_space, Conv2DSpace):
                    return (NotImplementedError,
                            "CompositeSpace does not know how to format as "
                            "Conv2DSpace")

                for fs, fb in safe_zip(from_space.components, from_batch):
                    error, message = get_expected_error(fs, fb, to_space)
                    if error is not None:
                        return error, message

                if isinstance(to_space, VectorSpace) and \
                   contains_different_dtypes(from_space) and \
                   to_space.dtype is None:
                    return (TypeError,
                            "Tried to format components with differing dtypes "
                            "into a VectorSpace with no dtype of its own. "
                            "dtypes: ")

                return None, None

            # simple -> composite
            if isinstance(to_space, CompositeSpace):

                if isinstance(from_space, VectorSpace) and \
                   isinstance(from_batch, theano.sparse.SparseVariable):
                    assert from_space.sparse
                    return (UserWarning,
                            'Formatting from a sparse VectorSpace to a '
                            'CompositeSpace is currently (2 Jan 2014) a '
                            'non-differentiable action. This is because it '
                            'calls slicing operations on a sparse batch '
                            '(e.g. "my_matrix[r:R, c:C]", which Theano does '
                            'not yet have a gradient operator for. If '
                            'autodifferentiation is reporting an error, '
                            'this may be why.')

                if isinstance(from_space, Conv2DSpace):
                    return (NotImplementedError,
                            "Conv2DSpace does not know how to format as "
                            "CompositeSpace")

                for ts in to_space.components:
                    error, message = get_expected_error(from_space,
                                                        from_batch,
                                                        ts)
                    if error is not None:
                        return error, message

                return None, None

            #
            # simple -> simple
            #

            def is_sparse(space):
                return isinstance(space, VectorSpace) and space.sparse

            def is_complex(arg):
                """
                Returns whether a space or a batch has a complex dtype.
                """
                return (arg.dtype is not None and
                        str(arg.dtype).startswith('complex'))

            if isinstance(from_batch, tuple):
                return (TypeError,
                        "This space only supports simple dtypes, but received "
                        "a composite batch.")

            if is_complex(from_batch) and not is_complex(from_space):
                return (TypeError,
                        "This space has a non-complex dtype (%s), and "
                        "thus cannot support complex batches of type %s." %
                        (from_space.dtype, from_batch.dtype))

            if from_space.dtype is not None and \
               from_space.dtype != from_batch.dtype:
                return (TypeError,
                        "This space is for dtype %s, but recieved a "
                        "batch of dtype %s." %
                        (from_space.dtype, from_batch.dtype))

            if is_sparse(from_space) and isinstance(to_space, Conv2DSpace):
                return (TypeError,
                        "Formatting a SparseVariable to a Conv2DSpace "
                        "is not supported, since neither scipy nor "
                        "Theano has sparse tensors with more than 2 "
                        "dimensions. We need 4 dimensions to "
                        "represent a Conv2DSpace batch")

            if is_complex(from_space) and not is_complex(to_space):
                if is_symbolic_batch(from_batch):
                    return (TypeError,
                            "Casting from complex to real is ambiguous")
                else:
                    return (np.ComplexWarning,
                            "Casting complex values to real discards the "
                            "imaginary part")

            return None, None

        def get_expected_formatted_dtype(from_batch, to_space):
            """
            Returns the expected dtype of the batch returned from a call to
            from_batch.format_as(batch, to_space). If the returned batch is a
            nested tuple, the expected dtype will also a nested tuple.
            """

            def get_single_dtype(batch):
                """
                Returns the dtype shared by all leaf nodes of the nested batch.
                If the nested batch contains differing dtypes, this throws an
                AssertionError. None counts as a different dtype than non-None.
                """
                if isinstance(batch, tuple):
                    assert len(batch) > 0
                    child_dtypes = tuple(get_single_dtype(b) for b in batch)
                    if any(c != child_dtypes[0] for c in child_dtypes[1:]):
                        return False

                    return child_dtypes[0]  # may be False, but that's correct.
                else:
                    return batch.dtype

            # composite -> composite
            if isinstance(from_batch, tuple) and \
               isinstance(to_space, CompositeSpace):
                return tuple(get_expected_formatted_dtype(b, s)
                             for b, s in safe_zip(from_batch,
                                                  to_space.components))
            # composite -> simple
            elif isinstance(from_batch, tuple):
                if to_space.dtype is not None:
                    return to_space.dtype
                else:
                    result = get_batch_dtype(from_batch)
                    if result is False:
                        raise TypeError("From_batch doesn't have a single "
                                        "dtype: %s" %
                                        str(get_batch_dtype(from_batch)))
                    return result

            # simple -> composite
            elif isinstance(to_space, CompositeSpace):
                return tuple(get_expected_formatted_dtype(from_batch, s)
                             for s in to_space.components)
            # simple -> simple with no dtype
            elif to_space.dtype is None:
                assert from_batch.dtype is not None
                return str(from_batch.dtype)
            # simple -> simple with a dtype
            else:
                return to_space.dtype

        from_batch = get_batch(from_space, using_numeric_batch)
        expected_error, expected_error_msg = get_expected_error(from_space,
                                                                from_batch,
                                                                to_space)

        # For some reason, the "with assert_raises(expected_error) as context:"
        # idiom isn't catching all the expceted_errors. Use this instead:
        if expected_error is not None:
            try:
                # temporarily upgrades warnings to exceptions within this block
                with warnings.catch_warnings():
                    warnings.simplefilter("error")
                    from_space._format_as(using_numeric_batch,
                                          from_batch,
                                          to_space)
            except expected_error as ex:
                assert str(ex).find(expected_error_msg) >= 0
            except Exception as unknown_ex:
                print("Expected exception of type %s, got %s." %
                      (expected_error.__name__, type(unknown_ex)))
                raise unknown_ex
            finally:
                return

        to_batch = from_space._format_as(using_numeric_batch,
                                         from_batch,
                                         to_space)
        expected_dtypes = get_expected_formatted_dtype(from_batch, to_space)
        actual_dtypes = get_batch_dtype(to_batch)

        assert expected_dtypes == actual_dtypes, \
            ("\nexpected_dtypes: %s,\n"
             "actual_dtypes: %s \n"
             "from_space: %s\n"
             "from_batch's dtype: %s\n"
             "from_batch is theano?: %s\n"
             "to_space: %s" % (expected_dtypes,
                               actual_dtypes,
                               from_space,
                               get_batch_dtype(from_batch),
                               is_symbolic_batch(from_batch),
                               to_space))
Example #8
0
    def test_format(from_space, to_space, using_numeric_batch):
        """
        Unit test for a call to from_space.np_format_as(batch, to_space)
        """

        # Type-checks the arguments
        for space, name in zip((from_space, to_space),
                               ("from_space", "to_space")):
            if not isinstance(space,
                              (VectorSpace, Conv2DSpace, CompositeSpace)):
                raise TypeError("This test only supports spaces of type "
                                "VectorSpace, Conv2DSpace, and "
                                "CompositeSpace, not %s's type %s" %
                                (name, type(space)))

        def get_batch(space, using_numeric_batch):
            """
            Uses space.get_origin_batch() to return a numeric batch,
            or space.get_theano_batch() to return a symbolic
            Uses a fallback dtype if the space itself doesn't have one.
            """
            def specifies_all_dtypes(space):
                """
                Returns True iff space has a completely specified dtype.
                """
                if isinstance(space, CompositeSpace):
                    return all(
                        specifies_all_dtypes(subspace)
                        for subspace in space.components)
                else:
                    return space.dtype is not None

            def replace_none_dtypes(dtype, fallback_dtype):
                """
                Returns dtype, with any Nones replaced by fallback_dtype.
                """

                if isinstance(dtype, tuple):
                    return tuple(
                        replace_none_dtypes(d, fallback_dtype) for d in dtype)
                else:
                    return fallback_dtype if dtype is None else dtype

            kwargs = {"batch_size": batch_size}

            # Use this when space doesn't specify a dtype
            fallback_dtype = theano.config.floatX

            if not specifies_all_dtypes(space):
                kwargs["dtype"] = replace_none_dtypes(space.dtype,
                                                      fallback_dtype)

            if using_numeric_batch:
                return space.get_origin_batch(**kwargs)
            else:
                # Sparse VectorSpaces throw an exception if batch_size is
                # specified
                if isinstance(space, VectorSpace) and space.sparse:
                    del kwargs["batch_size"]

                kwargs["name"] = "space-generated batch"
                return space.make_theano_batch(**kwargs)

        def get_expected_warning(from_space, from_batch, to_space):

            # composite -> composite
            if isinstance(from_space, CompositeSpace) and \
               isinstance(to_space, CompositeSpace):
                for fs, fb, ts in safe_zip(from_space.components, from_batch,
                                           to_space.components):
                    warning, message = get_expected_warning(fs, fb, ts)
                    if warning is not None:
                        return warning, message

                return None, None

            # composite -> simple
            if isinstance(from_space, CompositeSpace):
                for fs, fb in safe_zip(from_space.components, from_batch):
                    warning, message = get_expected_warning(fs, fb, to_space)
                    if warning is not None:
                        return warning, message

                return None, None

            # simple -> composite
            if isinstance(to_space, CompositeSpace):
                if isinstance(from_space, VectorSpace) and \
                   isinstance(from_batch, theano.sparse.SparseVariable):
                    assert from_space.sparse
                    return (UserWarning,
                            'Formatting from a sparse VectorSpace to a '
                            'CompositeSpace is currently (2 Jan 2014) a '
                            'non-differentiable action. This is because it '
                            'calls slicing operations on a sparse batch '
                            '(e.g. "my_matrix[r:R, c:C]", which Theano does '
                            'not yet have a gradient operator for. If '
                            'autodifferentiation is reporting an error, '
                            'this may be why.')

                for ts in to_space.components:
                    warning, message = get_expected_warning(
                        from_space, from_batch, ts)
                    if warning is not None:
                        return warning, message

                return None, None

            # simple -> simple
            return None, None

        def get_expected_error(from_space, from_batch, to_space):
            """
            Returns the type of error to be expected when calling
            from_space.np_format_as(batch, to_space). Returns None if no error
            should be expected.
            """
            def contains_different_dtypes(space):
                """
                Returns true if space contains different dtypes. None is
                considered distinct from all actual dtypes.
                """

                assert isinstance(space, CompositeSpace)

                def get_shared_dtype_if_any(space):
                    """
                    Returns space's dtype. If space is composite, returns the
                    dtype used by all of its subcomponents. Returns False if
                    the subcomponents use different dtypes.
                    """
                    if isinstance(space, CompositeSpace):
                        dtypes = tuple(
                            get_shared_dtype_if_any(c)
                            for c in space.components)
                        assert (len(dtypes) > 0)
                        if any(d != dtypes[0] for d in dtypes[1:]):
                            return False

                        return dtypes[0]  # could be False, but that's fine
                    else:
                        return space.dtype

                return get_shared_dtype_if_any(space) is False

            assert (isinstance(from_space, CompositeSpace) == isinstance(
                from_batch, tuple))

            # composite -> composite
            if isinstance(from_space, CompositeSpace) and \
               isinstance(to_space, CompositeSpace):
                for fs, fb, ts in safe_zip(from_space.components, from_batch,
                                           to_space.components):
                    error, message = get_expected_error(fs, fb, ts)
                    if error is not None:
                        return error, message

                return None, None

            # composite -> simple
            if isinstance(from_space, CompositeSpace):
                if isinstance(to_space, Conv2DSpace):
                    return (NotImplementedError,
                            "CompositeSpace does not know how to format as "
                            "Conv2DSpace")

                for fs, fb in safe_zip(from_space.components, from_batch):
                    error, message = get_expected_error(fs, fb, to_space)
                    if error is not None:
                        return error, message

                if isinstance(to_space, VectorSpace) and \
                   contains_different_dtypes(from_space) and \
                   to_space.dtype is None:
                    return (TypeError,
                            "Tried to format components with differing dtypes "
                            "into a VectorSpace with no dtype of its own. "
                            "dtypes: ")

                return None, None

            # simple -> composite
            if isinstance(to_space, CompositeSpace):

                if isinstance(from_space, VectorSpace) and \
                   isinstance(from_batch, theano.sparse.SparseVariable):
                    assert from_space.sparse
                    return (UserWarning,
                            'Formatting from a sparse VectorSpace to a '
                            'CompositeSpace is currently (2 Jan 2014) a '
                            'non-differentiable action. This is because it '
                            'calls slicing operations on a sparse batch '
                            '(e.g. "my_matrix[r:R, c:C]", which Theano does '
                            'not yet have a gradient operator for. If '
                            'autodifferentiation is reporting an error, '
                            'this may be why.')

                if isinstance(from_space, Conv2DSpace):
                    return (NotImplementedError,
                            "Conv2DSpace does not know how to format as "
                            "CompositeSpace")

                for ts in to_space.components:
                    error, message = get_expected_error(
                        from_space, from_batch, ts)
                    if error is not None:
                        return error, message

                return None, None

            #
            # simple -> simple
            #

            def is_sparse(space):
                return isinstance(space, VectorSpace) and space.sparse

            def is_complex(arg):
                """
                Returns whether a space or a batch has a complex dtype.
                """
                return (arg.dtype is not None
                        and str(arg.dtype).startswith('complex'))

            if isinstance(from_batch, tuple):
                return (TypeError,
                        "This space only supports simple dtypes, but received "
                        "a composite batch.")

            if is_complex(from_batch) and not is_complex(from_space):
                return (TypeError,
                        "This space has a non-complex dtype (%s), and "
                        "thus cannot support complex batches of type %s." %
                        (from_space.dtype, from_batch.dtype))

            if from_space.dtype is not None and \
               from_space.dtype != from_batch.dtype:
                return (TypeError,
                        "This space is for dtype %s, but recieved a "
                        "batch of dtype %s." %
                        (from_space.dtype, from_batch.dtype))

            if is_sparse(from_space) and isinstance(to_space, Conv2DSpace):
                return (TypeError,
                        "Formatting a SparseVariable to a Conv2DSpace "
                        "is not supported, since neither scipy nor "
                        "Theano has sparse tensors with more than 2 "
                        "dimensions. We need 4 dimensions to "
                        "represent a Conv2DSpace batch")

            if is_complex(from_space) and not is_complex(to_space):
                if is_symbolic_batch(from_batch):
                    return (TypeError,
                            "Casting from complex to real is ambiguous")
                else:
                    return (np.ComplexWarning,
                            "Casting complex values to real discards the "
                            "imaginary part")

            return None, None

        def get_expected_formatted_dtype(from_batch, to_space):
            """
            Returns the expected dtype of the batch returned from a call to
            from_batch.format_as(batch, to_space). If the returned batch is a
            nested tuple, the expected dtype will also a nested tuple.
            """
            def get_single_dtype(batch):
                """
                Returns the dtype shared by all leaf nodes of the nested batch.
                If the nested batch contains differing dtypes, this throws an
                AssertionError. None counts as a different dtype than non-None.
                """
                if isinstance(batch, tuple):
                    assert len(batch) > 0
                    child_dtypes = tuple(get_single_dtype(b) for b in batch)
                    if any(c != child_dtypes[0] for c in child_dtypes[1:]):
                        return False

                    return child_dtypes[0]  # may be False, but that's correct.
                else:
                    return batch.dtype

            # composite -> composite
            if isinstance(from_batch, tuple) and \
               isinstance(to_space, CompositeSpace):
                return tuple(
                    get_expected_formatted_dtype(b, s)
                    for b, s in safe_zip(from_batch, to_space.components))
            # composite -> simple
            elif isinstance(from_batch, tuple):
                if to_space.dtype is not None:
                    return to_space.dtype
                else:
                    result = get_batch_dtype(from_batch)
                    if result is False:
                        raise TypeError("From_batch doesn't have a single "
                                        "dtype: %s" %
                                        str(get_batch_dtype(from_batch)))
                    return result

            # simple -> composite
            elif isinstance(to_space, CompositeSpace):
                return tuple(
                    get_expected_formatted_dtype(from_batch, s)
                    for s in to_space.components)
            # simple -> simple with no dtype
            elif to_space.dtype is None:
                assert from_batch.dtype is not None
                return str(from_batch.dtype)
            # simple -> simple with a dtype
            else:
                return to_space.dtype

        from_batch = get_batch(from_space, using_numeric_batch)
        expected_error, expected_error_msg = get_expected_error(
            from_space, from_batch, to_space)

        # For some reason, the "with assert_raises(expected_error) as context:"
        # idiom isn't catching all the expceted_errors. Use this instead:
        if expected_error is not None:
            try:
                # temporarily upgrades warnings to exceptions within this block
                with warnings.catch_warnings():
                    warnings.simplefilter("error")
                    from_space._format_as(using_numeric_batch, from_batch,
                                          to_space)
            except expected_error as ex:
                assert str(ex).find(expected_error_msg) >= 0
            except Exception as unknown_ex:
                print("Expected exception of type %s, got %s." %
                      (expected_error.__name__, type(unknown_ex)))
                raise unknown_ex
            finally:
                return

        to_batch = from_space._format_as(using_numeric_batch, from_batch,
                                         to_space)
        expected_dtypes = get_expected_formatted_dtype(from_batch, to_space)
        actual_dtypes = get_batch_dtype(to_batch)

        assert expected_dtypes == actual_dtypes, \
            ("\nexpected_dtypes: %s,\n"
             "actual_dtypes: %s \n"
             "from_space: %s\n"
             "from_batch's dtype: %s\n"
             "from_batch is theano?: %s\n"
             "to_space: %s" % (expected_dtypes,
                               actual_dtypes,
                               from_space,
                               get_batch_dtype(from_batch),
                               is_symbolic_batch(from_batch),
                               to_space))