#!/usr/bin/env python
# coding: utf-8
# ## Faster wedge
# Going to look at faster ways to make wedges.
# In[1]:
get_ipython().run_line_magic('', 'matplotlib inline')
import matplotlib.pyplot as plt
import numpy as np
# Set up the model parameters.
# In[2]:
length = 100.# x range
width = 100. # y range
depth = 240. # z range
# Define the plotting function.
# In[3]:
def plot_slices(data, interpolation='nearest', cmap='Greys', vmin=0., vmax=1.):
plt.figure(figsize=(16,12))
for j in range(3):
plt.subplot(3,4,j+1)
plt.title('y = ' + str(int(j*(length/3))))
plt.imshow(data[:,:,int(j*(length/3))], interpolation=interpolation, cmap=cmap, vmax=vmax, vmin=vmin)
plt.subplot(3,4,4)
plt.title('y = ' + str(int(length-1)))
plt.imshow(data[:,:,-1], interpolation=interpolation, cmap=cmap, vmax=vmax, vmin=vmin)
for j in range(3):
plt.subplot(3,4,5+j)
plt.title('x = ' + str(int(j*(width/3))))
plt.imshow(data[:,int(j*(width/3)),:], interpolation=interpolation, cmap=cmap, vmax=vmax, vmin=vmin)
plt.subplot(3,4,8)
plt.title('x = ' + str(int(width-1)))
plt.imshow(data[:,-1,:], interpolation=interpolation, cmap=cmap, vmax=vmax, vmin=vmin)
for j in range(3):
plt.subplot(3,4,9+j)
plt.title('z = ' + str(1 + int(1 + depth/3 + j*depth/(3*3))))
plt.imshow(data[1 + int(depth/3 + j*depth/(3*3)),:,:], interpolation=interpolation, cmap=cmap, vmax=vmax, vmin=vmin)
plt.subplot(3,4,12)
plt.title('z = ' + str(int(2*depth/3 - 1)))
plt.imshow(data[int(2*depth/3 - 1),:,:], interpolation=interpolation, cmap=cmap, vmax=vmax, vmin=vmin)
plt.show()
# ## Original formulation
# Let's start with the wedge code from version 1 of Spectral_wedge.ipynb
. The main difference is that we will need to use the rock properties directly, rather than building an array of int
s first.
# In[5]:
rocks = [(2800, 2850), (2800, 2850), (2400, 2450), (2400,2450)]
def make_ai(rocks):
rx = np.array(rocks)
return rx[:,0] * rx[:,1] / 10e6
ai = make_ai(rocks)
print(ai)
# In[6]:
def make_earth(depth, length, width, ai):
earth = np.ones((depth,length,width))
for xslice in range(int(length)):
thickness = (depth/3)*(1 + xslice/length)
value = (ai[1]*xslice/length) + (ai[2]*(length-xslice)/length)
earth[:,xslice,:] *= value
# Draw the bottom of the wedge
value = ai[0]
for (z,y),v in np.ndenumerate(earth[:,xslice,:]):
if z > thickness:
earth[z,xslice,y] = value
# Put the top on
value = ai[3]
for (z,y),v in np.ndenumerate(earth[:,xslice,:]):
if z < depth/3:
earth[z,xslice,y] = value
return earth
# In[7]:
get_ipython().run_line_magic('timeit', 'earth = make_earth(depth, length, width, ai)')
# In[7]:
earth = make_earth(depth, length, width, ai)
# In[8]:
earth.shape
# ## Index the array with a list - SLOW
# You can index an array with an array-like object — a list or another array.
# In[8]:
a = np.array([1,2,3,4,5,6,7,8]) # A 1D array
b = [1,3,5,7]
print a[b]
d = np.array([[1,2,3,4],[5,6,7,8]]) # A 2D array...
e = [[0,1],[1,3]] # Needs 2 indices
print d[e]
# Now let's try using index lists, building the indices with nested list comprehensions.
# In[9]:
def use_index_lists(depth, length, width, ai):
earth = np.ones((depth,length,width))
# Bottom indices
indices = [[[x for x in range(int(length))] for y in range(int(width))] for z in range(int(depth/2))]
earth[indices] = 2.
return earth
# In[ ]:
# Only getting started, let's test it...
get_ipython().run_line_magic('timeit', 'earth_index_lists = use_index_lists(depth, length, width, ai)')
# In[ ]:
earth_index_lists = use_index_lists(depth, length, width, ai)
# In[ ]:
earth_index_lists.shape
# That was unbelievably slow. Forget it.
# ## Fewer loops - FAST
# It occurs to me I have too many loops. I think I need one, because the values I want vary with index.
#
# I just realized that instead of enumerating and deciding if the *z*-index is greater than thickness, say, I can just slice with thickness as the index. Doh! NumPy is awesome...
# In[9]:
def one_loop(depth, length, width, ai):
earth = np.ones((depth,length,width))
d = depth/3
for xslice in range(int(length)):
xl = xslice/length
thickness = d*(1 + xl)
mix = (ai[1]*xl) + (ai[2]*(length-xslice)/length)
# Draw wedge
earth[:,xslice,:] *= mix
# Put the top on
earth[:d,:,:] = ai[3]
return earth
# In[10]:
get_ipython().run_line_magic('timeit', 'one_loop(depth, length, width, ai)')
# Holy crap, that's a bit better.
# In[11]:
earth = one_loop(depth, length, width, ai)
# ## Check plot
# In[12]:
plot_slices(earth, interpolation='nearest', cmap='jet')
# ## The final frontier
# The last tricky bit is the outer loop. It's only there to loop over the *x*-slices. The tricky part is that the wedge thickness calculation depends on the index of the slice. I think numpy.indices
can provide this aspect and avoid the loop...
# In[13]:
zs, xs, ys = np.indices((4,3,2))
print "x values:\n", xs
print "\ny values:\n", ys
print "\nz values:\n", zs
# In[8]:
def no_loop(depth, length, width, ai):
# Make an earth full of ones
earth = np.ones((depth,length,width))
# Set up the gradient, which we'll then crop, so to speak
mix = np.array([(ai[1]*x/length) + (ai[2]*(length-x)/length) for x in range(int(length))])
earth *= mix[:, np.newaxis]
# Now make the wedge
d = depth/3
thickness = [d*(1 + x/length) for x in range(int(length))]
# Make a set of index arrays
zs = np.indices(earth.shape)[0]
# Make a boolean array map of stuff below the wedge
below = zs > thickness
# I don't know why I have to do this but I do
below = np.swapaxes(below, axis1=1, axis2=2)
# Set those values to the lowermost rock AI
earth[below] = ai[0]
# Put the top layer on
earth[:d,:,:] = ai[3]
return earth
# In[9]:
get_ipython().run_line_magic('timeit', 'earth = no_loop(depth, length, width, ai)')
# In[ ]:
def no_loop(depth, length, width, ai):
# Make an earth full of ones
earth = np.ones((depth,length,width))
# Set up the gradient, which we'll then crop, so to speak
mix = np.array([(ai[1]*x/length) + (ai[2]*(length-x)/length) for x in range(int(length))])
earth *= mix[:, np.newaxis]
# Now make the wedge
d = depth/3
thickness = [d*(1 + x/length) for x in range(int(length))]
# Make a set of index arrays
zs = np.indices(earth.shape)[0]
# Make a boolean array map of stuff below the wedge
below = zs > thickness
# I don't know why I have to do this but I do
below = np.swapaxes(below, axis1=1, axis2=2)
# Set those values to the lowermost rock AI
earth[below] = ai[0]
# Put the top layer on
earth[:d,:,:] = ai[3]
return earth
# In[11]:
depth, length, width, ai
# In[ ]:
# In[16]:
earth = no_loop(depth, length, width, ai)
# In[17]:
plot_slices(earth, interpolation='nearest', cmap='jet')
# Amazing — the for
loop is 3 times faster! And I think it's the indices
part that is slowing it down, because without that section (the line with np.indices
and the three lines after it), it only takes 9.5 ms to execute.
# ## Conclusion
# Assuming I'm not doing something stupid in this code, it seems it's not always faster with indexing.