summaryrefslogtreecommitdiff
path: root/advanced_python/scipy.rst
blob: fd0508a4f5d344037141474196de0fb8a554150f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
In this section we shall look at using scipy to do various common
scientific computation tasks. We shall be looking at solving equations
(linear and non-linear) and solving ODEs. We shall also briefly look at
FFTs. 

Solving Equations
=================

Let us begin with solving equations, specifically linear equations. 

Consider the set of equations,
::

    3x + 2y -z = 1, 
    2x-2y + 4z = -2, 
    -x+ 1/2 y-z = 0

We shall use the ``solve`` function, to solve the given system of linear
equations. ``solve`` requires the coefficients and the constants to be in
the form of matrices, of the form ``Ax = b`` to solve the system. 

We begin by entering the coefficients and the constants as matrices.

::

    A = array([[3,2,-1], 
               [2,-2,4],
               [-1, 0.5, -1]])

A is a 3X3 matrix of the coefficients of x, y and z

::

    b = array([1, -2, 0])

Now, we can use the solve function to solve the given system. 

::
    
    x = solve(A, b)

Type x, to look at the solution obtained. 

The equation is of the form ``Ax = b``, so we verify the solution by
obtaining a matrix product of ``A`` and ``x``, and comparing it with ``b``.
As we have seen earlier, we should use the dot function, for a matrix
product and not the * operator.

::

    Ax = dot(A, x)
    Ax

The result ``Ax``, doesn't look exactly like ``b``, but if we carefully
observe, we will see that it is the same as ``b``. To save ourself all this
trouble, we can use the ``allclose`` function.

``allclose`` checks if two matrices are close enough to each other (with-in
the specified tolerance level). Here we shall use the default tolerance
level of the function.

::
    allclose(Ax, b)

The function returns ``True``, which implies that the product of ``A`` &
``x`` is very close to the value of ``b``. This validates our solution. 

Let's move to finding the roots of a polynomial. We shall use the ``roots``
function for this.

The function requires an array of the coefficients of the polynomial in the
descending order of powers. Consider the polynomial x^2-5x+6 = 0

::
    
    coeffs = [1, -5, 6]
    roots(coeffs)

As we can see, roots returns the result in an array. It even works for
polynomials with imaginary roots.

::

    roots([1, 1, 1])

As you can see, the roots of that equation are complex. 

What if, we want the solution of non linear equations?

For that we shall use the ``fsolve`` function. We shall use the equation
sin(x)+cos^2(x), as an example. ``fsolve`` is not part of the pylab package
which we imported at the beginning, so we will have to import it. It is
part of ``scipy`` package. Let's import it,

::

    from scipy.optimize import fsolve

We shall look at the details of importing, later.

Now, let's look at the documentation of fsolve by typing fsolve?    

::
    
    fsolve?

As mentioned in documentation the first argument, ``func``, is a python
function that takes atleast one argument. The second argument, ``x0``, is
the initial estimate of the roots of the function. Based on this initial
guess, ``fsolve`` returns a root.

Let's define a function called f, that returns the value of
``(sin(x)+cos^2(x))`` evaluated at the input value ``x``. 

::

    def f(x):
        return sin(x)+cos(x)*cos(x)

We can test our function, by calling it with an argument for which the
output value is known, say x = 0. We can see that 

Let's check our function definition, by calling it with 0 as an argument.

::

    f(0)


We can see that the output is 1 as expected, since sin(x) + cos^2(x) has a
value of 1, when x = 0.

Now, that we have our function, we can use ``fsolve`` to obtain a root of
the expression sin(x)+cos^2(x). Let's use 0 as our initial guess.

::

    fsolve(f, 0)

fsolve has returned a root of sin(x)+cos^2(x) that is close to 0. 

That brings us to the end of our discussion on solving equations. We
discussed solution of linear equations, finding roots of polynomials and
non-linear equations. 


ODEs
====

Let's see how to solve Ordinary Differential Equations (ODEs), using
Python. Let's consider the classic problem of the spread of an epidemic in
a population, as an example. 

This is given by the ordinary differential equation ``dy/dt = ky(L-y)``
where L is the total population and k is an arbitrary constant. 

For our problem, let us use L=250000, k=0.00003. Let the boundary condition
be y(0)=250.

We shall use the ``odeint`` function to solve this ODE. As before, this
function resides in a submodule of SciPy and doesn't come along with Pylab.
We import it, 
::

    from scipy.integrate import odeint

We can represent the given ODE as a Python function. This function takes
the dependent variable y and the independent variable t as arguments and
returns the ODE.

::

    def epid(y, t):
        k = 0.00003
        L = 250000
        return k*y*(L-y)

Independent variable t can be assigned the values in the interval of 0 and
12 with 60 points using linspace:

    In []: t = linspace(0, 12, 60)

Now obtaining the solution of the ode we defined, is as simple as calling
the Python's ``odeint`` function which we just imported

::
    
    y = odeint(epid, 250, t)

We can plot the the values of y against t to get a graphical picture our ODE.

::

    plot(y, t)

Let's now try and solve an ODE of second order. Let's take the example of a
simple pendulum. 

The equations can be written as a system of two first order ODEs

    d(theta)/dt = omega
    
and

    d(omega)/dt = - g/L sin(theta)

Let us define the boundary conditions as: at t = 0, theta = theta-naught =
10 degrees and omega = 0

Let us first define our system of equations as a Python function,
``pend_int``. As in the earlier case of single ODE we shall use ``odeint``
function to solve this system of equations. 

::

    def pend_int(initial, t):
        theta = initial[0]
        omega = initial[1]
        g = 9.81
        L = 0.2
        f=[omega, -(g/L)*sin(theta)]
        return f
    
It takes two arguments. The first argument itself containing two dependent
variables in the system, theta and omega. The second argument is the
independent variable t.

In the function we assign the first and second values of the initial
argument to theta and omega respectively. Acceleration due to gravity, as
we know is 9.81 meter per second sqaure. Let the length of the the pendulum
be 0.2 meter.

We create a list, f, of two equations which corresponds to our two ODEs,
that is ``d(theta)/dt = omega`` and ``d(omega)/dt = - g/L sin(theta)``. We
return this list of equations f.

Now we can create a set of values for our time variable t over which we need
to integrate our system of ODEs. Let us say,

::

    t = linspace(0, 20, 100)

We shall assign the boundary conditions to the variable initial.

::

    initial = [10*2*pi/360, 0]

Now solving this system is just a matter of calling the odeint function with
the correct arguments.

::

    pend_sol = odeint(pend_int, initial,t)

    plot(pend_sol)

This gives us 2 plots, omega vs t and theta vs t.

That brings us to the end of our discussion on ODEs and solving them in
Python. 

.. 
   Local Variables:
   mode: rst
   indent-tabs-mode: nil
   sentence-end-double-space: nil
   fill-column: 75
   End: