Arbitrary tensor contraction.

This PR adds support for contraction along arbitrary dimensions of a qobj, whether those dimensions are state, operator or superoperator indices. The generalization is provided by a small number of utility functions for manipulation ``dims`` properties with few assumptions. Unfortunately, due to the limitations of ``scipy.sparse``, this functionality is implemented on dense arrays, but for small matrices, it works suitably fast.
Why create a view of `qobj.data.todense()`? The `todense` will anyways create a new array with a copy of the sparse matrix data `qobj.data`.