Lab2

Here are my suggested edits to lab2. After talking to the TAs and students in the class, we decided that timing things was too complicated for a beginning lab (not to mention that it isn't part of this lab's objective), so we removed those problems. The plan is to include timing in the profiling lab.
Yes. Arrays, in memory, are always stored in 1D connected blocks. Multidimensional arrays (at least in the case of numpy, though this is true in a lot of cases) are really just objects that index the 1D block as if it had multiple dimensions. When an array views memory in a contiguous fashion, the elements of the array are, in some sense, adjacent to one another in memory. The problem comes when slices are created. For example, if you run

    a = np.arange(6).reshape((2, 3))
    b = a[:,::2]

you will now have created an array object that views only the elements [0, 2, 3, 5] of the flattened array in memory, which actually contains the array [0, 1, 2, 3, 4, 5]. This works fine for NumPy's indexing scheme because it just stores the fact that, in the columns dimension, it is only taking every other column. The problem for `ravel` is that there is not any evenly spaced way to view the non-contiguous array with its current memory layout, so a copy must be made.

Non contiguous arrays do not always trigger copies though. For example, had we written

    a = np.arange(8)
    b = a[:,::2]

we would be able to call `b.ravel()` without creating a copy since the requested elements are, in fact, evenly spaced in memory.

This statement was deliberately left vague in this lab to avoid having to dive into a discussion that was a little too advanced for this lab. The lab was already very long as well, so we chose to leave the full description out. We did mention that a view is created "when possible" for technical correctness (and because it is useful functionality).

If you need to iterate through an array as if it were flat, you could just try using `b.flat` instead since it returns an iterator object and does not involve creating a copy. The `flat` iterator also allows operations such as slicing, which can be very useful as well.