-->

Friday, 25 October 2013

IPython Notebook Playing With Functions

In [149]:
%pylab inline
from matplotlib import mpl,pyplot,colors
import numpy as np
from sympy import *
from sympy.interactive import printing
import functools
Populating the interactive namespace from numpy and matplotlib

WARNING: pylab import has clobbered these variables: ['prod', 'plotting', 'cosh', 'Circle', 'power', 'diag', 'sinh', 'trunc', 'binomial', 'plot', 'eye', 'det', 'tan', 'product', 'gamma', 'roots', 'vectorize', 'zeros', 'interactive', 'conjugate', 'take', 'solve', 'trace', 'beta', 'colors', 'ones', 'multinomial', 'transpose', 'cos', 'diff', 'invert', 'pi', 'tanh', 'Polygon', 'reshape', 'sqrt', 'source', 'add', 'test', 'poly', 'mod', 'sign', 'log', 'var', 'seterr', 'flatten', 'floor', 'nan', 'exp', 'sin']
`%pylab --no-import-all` prevents importing * from pylab and numpy

In [150]:
x = Symbol('x')

class Function(object):
    def __init__(self, coeff, start=-5, end = 5, points = 20):
        # more points = more accurate but take more time
        self.points =(end-start)* points
        
        # tuples in the form (coefficient, power of x)
        l = len(coeff)
        self.coeff = [(value, l - 1 - index) for index, value in enumerate(coeff)]
        
        
        self.X = np.linspace(start, end, self.points)
        
        
        # coefficients for the first and second derivatives
        self.D1 = [(a*b, b-1) for a,b in self.coeff[:-1]]
        self.D2 = [(a*b, b-1) for a,b in self.D1[:-1]]
        
        # set the Y values
        self.Y = np.zeros(self.points)
        for a,b in self.coeff:            
            self.Y = self.Y +a*(self.X**b) 
            
            
       
    def y(self,coeff, change = None):
        y = np.zeros(self.points)
        
        for a,b in coeff:
            if b == change:
                print "changing",change
                y = y -a*(self.X**b)
            else:
                
                y = y +a*(self.X**b)
            
        return y
        
        
    def values(self):
        pass
    
    def d1(self):
        return self.D1
    
    def d2(self):
        return self.D2
    
    
    # plot the function and its first derivative
    def plotD1(self):
        pyplot.plot(self.X,self.Y)
        pyplot.plot(self.X,self.y(self.D1))
    
    # plot the function and its first AND second derivatives
    def plotD1D2(self):
        pyplot.plot(self.X,self.Y)
        pyplot.plot(self.X,self.y(self.D1))
        pyplot.plot(self.X,self.y(self.D2))
        grid(True)
    
    def plot(self, coeff = None):
        pyplot.plot(self.X,self.Y)
        pyplot.grid()
            
        pyplot.show()
        
    def change(self,co):
        pyplot.plot(self.X,self.Y)
        #pyplot.plot(self.X,self.y(self.D1))
        #pyplot.plot(self.X,self.y(self.D1,co-1))
        pyplot.grid()
        pyplot.plot(self.X,self.y(self.coeff, co))
        
    def changeWithDerivative(self,co):
        x1 = self.X
        
        #plt.subplot(2, 1, 1)
        plt.plot(x1, self.Y, 'b-')
        plt.plot(x1, self.y(self.D1), 'b-')
        
        plt.grid(which='major', color='k', linestyle='-')

        #plt.subplot(2, 1, 2)
        plt.plot(self.X,self.y(self.coeff, co), 'r-')
        plt.plot(self.X, self.y(self.D1,co-1), 'r-')
        
        
        plt.grid(which='major', color='k', linestyle='-')

        plt.show()
    
             

def functionFromRoots(listOfRoots, start=-5, end = 5, points = 10):
    factors = [(x-a) for a in listOfRoots]
    print "Here are the factors:", factors
    
    
    def ffr(a,b):
        return expand(a*b)
    poly = functools.reduce(lambda a,b: ffr(a,b), factors)
    
    print "Here is the polynomial", latex(poly)
    
    poly = Poly(poly)
    
    return Function(poly.coeffs(), start, end, points)
    
roots = [1,-1,4]
ffr = functionFromRoots(roots)
ffr.plot()   
    
l = [1,-1,-14,0]
a = Function(l)


#a.plotD1D2()
Here are the factors: [x - 1, x + 1, x - 4]
Here is the polynomial x^{3} - 4 x^{2} - x + 4

In [151]:
ff = functionFromRoots([1,2,3])

z = Poly(expand((x+1)*(x+2)*(x+3)))

print z.coeffs()
Here are the factors: [x - 1, x - 2, x - 3]
Here is the polynomial x^{3} - 6 x^{2} + 11 x - 6
[1, 6, 11, 6]

Drawing a Polynomial From Roots

When working with larger degree polynomials we could just enter more numbers into the Function constructor. For example:
f = Function[1,5,-7,12])
The higher the degree the harder it is to intuitively get real roots in a narrow range. For this we can use functionFromRoots(listOfRoots) which takes a list of roots as its argument.
In [152]:
roots = [1,-1,4]
ffr = functionFromRoots(roots)
ffr.plot()
Here are the factors: [x - 1, x + 1, x - 4]
Here is the polynomial x^{3} - 4 x^{2} - x + 4

Using [1,-1,4] makes it hard to look at the key parts so I will also use 'start = -2' to bring the picture into focus
In [153]:
ffr = functionFromRoots(roots, start=-2)
ffr.plotD1D2()
Here are the factors: [x - 1, x + 1, x - 4]
Here is the polynomial x^{3} - 4 x^{2} - x + 4

Change The Sign Of a Coefficient

What happens if we change the sign for each part. First lets plot a function.
Note that you can change the numbers here and then go 'Cell' > 'Run All Below' to change everything in one go.
In [170]:
m = Function([2,-1,2])
m.plot()
Ok let's change the sign by calling the power of the exponent of the cell we wish to change.
m.change(0) changes the constant, m.change(1) changes the coeff of the x ... where m is the name of our function
In [171]:
m.change(0)
changing 0

All the constant does is move a curve up or down. Note it may look like the curves are getting closer together but if you check where they cross grid lines you will see that they are not.
Next up change the x part.
In [172]:
m.change(1)
changing 1

Changing the x part will move the graph either left or right. Can you figure out which?
Hint: If the coefficient of x is positive then it is pulling the graph down until it reaches 0. After 0 it is pulling the graph up. The converse is true for a negative coefficient.
Perhaps it is easier to see with derivatives.
In [173]:
m.changeWithDerivative(1)
changing 1
changing 0

Changing the sign of the x part will change the constant part of the derivative. We have already learned that this shifts things up and down.
So if changing the constant moves us vertically and changing the x part moves us horizontally then what is left for the x^2 part?
In [175]:
m.change(2)
changing 2

Arrow Key Nav