ENH: add `mdot`: fast dot with multiple arguments.

This is not ready to be merged!

`mdot` makes it easy to chain `dot`. Think of `mdot` as this:

    def mdot(*args): return reduce(np.dot, args)

However, `mdot` uses the fact that matrix multiplication is associative and
finds the optimal order of multiplications with dynamic programing before
doing the mutiplication. This can speed up the multiplication quite a bit.

The algorithm follows:
Cormen, "Introduction to Algorithms", Chapter 15.2, p. 370-378

Note: currently `mdot` only works with arrays which have `ndim == 2` to avoid
ambiguities of `dot`. Also see the discussion in #4311.

TODO:
- [x] Decide if `ndim != 2` should be ok
- [x] Add unittests
- [x] decide on name. possible names are:
  - `mdot`: think "multi dot"
  - `multidot`: as above :)
  - `multi_dot`: as above :)
  - `chain_dot`
  - `fdot`: think "fast dot" (should the parameter optimize be optional then? I don't think so.)
  - `ddot`: think "multiple d for multiple dot"
  - `cdot`: think "chain dot"
  - `odot`: think "optimal do

> If there are two different possible optimization criteria - which I doubt,

@njsmith I'm pretty sure @jaimefrio's example shows there are.  If all of the matrices in his example are nearly square (say order 1000 plus or minus up to three or four rows and columns) then reordering to slightly reduce the number of operations would increase the required amount of temporary storage.