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.
When I tried on my system, ``todense()`` returned an ``np.matrix`` instead of an ``np.ndarray``. The former doesn't support tensor ranks other than 2, and so I had to ``view`` to get it to work on my local system.