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)
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
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
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
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.
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))
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))