bgneal@21: """This module contains code from
bgneal@21: Think Python by Allen B. Downey
bgneal@21: http://thinkpython.com
bgneal@21: 
bgneal@21: Copyright 2012 Allen B. Downey
bgneal@21: License: GNU GPLv3 http://www.gnu.org/licenses/gpl.html
bgneal@21: 
bgneal@21: This is 3.6 exercise 6:
bgneal@21: 
bgneal@21: "Test the performance of LinearMap, BetterMap and HashMap; see if you can
bgneal@21: characterize their order of growth.  You can download my map implementations
bgneal@21: from thinkcomplex.com/Map.py, and the code I used in this section from
bgneal@21: thinkcomplex.com/listsum.py.
bgneal@21: 
bgneal@21: You will have to find a range of n that is big enough to show asymptotic
bgneal@21: behavior, but small enough to run quickly."
bgneal@21: 
bgneal@21: """
bgneal@21: import random
bgneal@21: import timeit
bgneal@21: 
bgneal@21: import matplotlib.pyplot as pyplot
bgneal@21: 
bgneal@21: from ch3ex4 import LinearMap, BetterMap, HashMap
bgneal@21: 
bgneal@21: 
bgneal@21: MAP = None
bgneal@21: VALUES = None
bgneal@21: 
bgneal@21: def test_map():
bgneal@21: 
bgneal@21:     MAP.get(random.choice(VALUES))
bgneal@21: 
bgneal@21: 
bgneal@21: def test_func():
bgneal@21: 
bgneal@21:     elapsed = timeit.timeit('test_map()',
bgneal@21:         setup='from __main__ import test_map', number=100)
bgneal@21:     return elapsed
bgneal@21: 
bgneal@21: 
bgneal@21: def test(map_class):
bgneal@21:     """Tests the given function with a range of values for n.
bgneal@21: 
bgneal@21:     Returns a list of ns and a list of run times.
bgneal@21:     """
bgneal@21:     global MAP, VALUES
bgneal@21: 
bgneal@21:     klass = eval(map_class)
bgneal@21: 
bgneal@21:     d = {'LinearMap': 100000, 'BetterMap': 100000, 'HashMap': 100000}
bgneal@21:     factor = d[map_class]
bgneal@21: 
bgneal@21:     # test (f) over a range of values for (n)
bgneal@21:     ns = []
bgneal@21:     ts = []
bgneal@21:     for i in range(2, 25):
bgneal@21:         n = factor * i
bgneal@21: 
bgneal@21:         MAP = klass()
bgneal@21:         VALUES = range(n)
bgneal@21:         random.shuffle(VALUES)
bgneal@21:         for v in VALUES:
bgneal@21:             MAP.add(v, v)
bgneal@21: 
bgneal@21:         t = test_func()
bgneal@21: 
bgneal@21:         print n, t
bgneal@21:         ns.append(n)
bgneal@21:         ts.append(t)
bgneal@21: 
bgneal@21:     return ns, ts
bgneal@21: 
bgneal@21: 
bgneal@21: def plot(ns, ts, label, color='blue', exp=1.0):
bgneal@21:     """Plots data and a fitted curve.
bgneal@21: 
bgneal@21:     ns: sequence of n (problem size)
bgneal@21:     ts: sequence of t (run time)
bgneal@21:     label: string label for the data curve
bgneal@21:     color: string color for the data curve
bgneal@21:     exp: exponent (slope) for the fitted curve
bgneal@21:     """
bgneal@21:     tfit = fit(ns, ts, exp)
bgneal@21:     pyplot.plot(ns, tfit, color='0.7', linewidth=2, linestyle='dashed')
bgneal@21:     pyplot.plot(ns, ts, label=label, color=color, linewidth=3)
bgneal@21: 
bgneal@21: 
bgneal@21: def fit(ns, ts, exp=1.0, index=-1):
bgneal@21:     """Fits a curve with the given exponent.
bgneal@21: 
bgneal@21:     Use the given index as a reference point, and scale all other
bgneal@21:     points accordingly.
bgneal@21:     """
bgneal@21:     nref = ns[index]
bgneal@21:     tref = ts[index]
bgneal@21: 
bgneal@21:     tfit = []
bgneal@21:     for n in ns:
bgneal@21:         ratio = float(n) / nref
bgneal@21:         t = ratio**exp * tref
bgneal@21:         tfit.append(t)
bgneal@21: 
bgneal@21:     return tfit
bgneal@21: 
bgneal@21: 
bgneal@21: def save(root, exts=['eps', 'pdf']):
bgneal@21:     """Saves the current figure in the given formats.
bgneal@21: 
bgneal@21:     root: string filename root
bgneal@21:     exts: list of string extensions (specifying output formats).
bgneal@21:     """
bgneal@21:     for ext in exts:
bgneal@21:         filename = '%s.%s' % (root, ext)
bgneal@21:         print 'Writing', filename
bgneal@21:         pyplot.savefig(filename)
bgneal@21: 
bgneal@21: 
bgneal@21: def make_fig(funcs, scale='log', exp=1.0, filename=None):
bgneal@21:     pyplot.clf()
bgneal@21:     pyplot.xscale(scale)
bgneal@21:     pyplot.yscale(scale)
bgneal@21:     pyplot.title('')
bgneal@21:     pyplot.xlabel('n')
bgneal@21:     pyplot.ylabel('run time (s)')
bgneal@21: 
bgneal@21:     colors = ['blue', 'orange', 'green']
bgneal@21:     for func, color in zip(funcs, colors):
bgneal@21:         data = test(func)
bgneal@21:         plot(*data, label=func, color=color, exp=exp)
bgneal@21: 
bgneal@21:     pyplot.legend(loc=4)
bgneal@21: 
bgneal@21:     if filename:
bgneal@21:         save(filename)
bgneal@21:     else:
bgneal@21:         pyplot.show()
bgneal@21: 
bgneal@21: 
bgneal@21: def main(script):
bgneal@21:     make_fig(['LinearMap', 'BetterMap', 'HashMap'], scale='log', exp=1.0)
bgneal@21: 
bgneal@21: 
bgneal@21: if __name__ == '__main__':
bgneal@21:     import sys
bgneal@21:     main(*sys.argv)
bgneal@21: