Enable caching of reshape, transpose, and tocsr/csc operations

This enables efficient iterative workflows that make heavy use of csr/csc operations, such as tensordot. This maintains a cache of recent results of reshape and transpose so that operations like tensordot (which uses both internally) store efficiently stored representations for repeated use. This can significantly cut down on computational costs in common numeric algorithms.

However, this also assumes that neither this object, nor the downstream objects will have their data mutated.


>>> s.enable_caching()  
>>> csr1 = s.transpose((2, 0, 1)).reshape((100, 120)).tocsr()  
>>> csr2 = s.transpose((2, 0, 1)).reshape((100, 120)).tocsr()  
>>> csr1 is csr2