## Description

Applied Programming Lab (EE2703)

1 Introduction

In the last assignment, we have seen the basics of python. In this assignment, you will get

introduced to scientific python. Make sure you have completed all the previous homework as

well as the assignment before getting started with this assignment.

2 OOP and Python

Just like C++, classes help you implement OOP in Python. Classes are the blueprint of

objects. They specify what the object can have. For example, say you have a class for Player.

The class will tell you that your object has a property named “position”. But does not say

what the “position” is. This is useful since a class can parent as many different objects as you

want. Each object can have a different “position”.

In [1]: class Player:

…: position_x = 0

…: position_y = 0

…:

…: def __init__(self, color):

…: self.color = color

…:

…: def move(self, distance, direction):

…: if direction == ’right’:

…: self.position_x += distance

…: elif …

…: …

Here, we defined a class Player. Let’s go line by line.

1

1. The first line says the it’s a definition of a class and the name of the class is Player

2. The next two lines define features of the class. The player has x and y positions. These

values are set to 0 by default.

3. init () is the constructor. The first argument to it is always self. self refers to the

object itself!

4. The constructor takes in one extra argument color. The interesting thing here is that,

the constructor is declaring a new feature for the class at run-time! In python, you can

add features to an object dynamically!

5. move() here is a member function of the class. Note that the first argument to a member

function should be self.

In [2]: p1 = Player(’red’) # Creating a player object

In [3]: p1.position_x

Out[3]: 0

In [4]: p1.move(2, ’right’) # Calling a member function

In [5]: p1.position_x

Out[5]: 2

Note that, we did not provide the parameter self here. Since first argument to member

function is always self, Python passes it automatically.

Though we are seeing Python classes for the first time, you have been using the power of classes

even without realizing it. For example, lists that you have been creating are actually objects

of the class “List”.

In [6]: l = [1, 4, 5, 2, 3]

In [7]: l.sort()

Here, you are creating an object of the class list in the first line ([…] is the constructor here)

and calling a member function of the object in the second line.

3 List unpacking

One peculiar thing about Python is that you can return multiple values from a function!

In [8]: def f():

…: return 1, 2, 3, 4

2

In [9]: r = f(); r

Out[9]: (1, 2, 3, 4)

In [10]: w, x, y, z = f()

f() is returning four values here. How the returned values get stored is heavily depended on

the calling statement. In the first call to f(), Python will compile all the return values and

give to us a tuple. But in the next call, return values are assigned to separate variables in the

order mentioned. What will happen if you try x, y = f()? What about x, *y, z = f() ?

This is called extended unpacking.

Python goes even further:

In [11]: a, b, c = [1, 2, 3]

In [12]: i = 1; j = 2

In [13]: i, j = j, i

What are the values of i and j now?

3.0.1 Homework

Given a list

l = [ [1, 2], ’hello’ ]

How can you extract the first character of ’hello’ only by using list unpacking? Do it in

single statement.

4 Scientific Python

What makes python amazing is the huge collection of modules that come with it. For example,

Modules like scipy, numpy, matplotlib, sympy and panda make it suitable for scientific computing. numpy helps with numerical computations, scipy provides special functions that will

help you with scientific computing and also adds complex number support to all the functions.

matplotlib provides sophisticated plotting capabilities.

Most of the underlying implementations of these libraries are in C. This ensures the efficiency

of these implementations. But to make your life easy, these C implementations are wrapped in

Python and given as python functions which you can directly call from python. Which means

they are efficient at the same time easy to use.

3

4.1 Arrays vs Lists

While Python lists are amazing by themselves, they are not always the best data type to

depend. The flexibility they provide makes them slow. To make things faster, numpy provides

array data type. numpy arrays are lot similar to C arrays. As I already mentioned, the speed

come from the fact that the underlying implementation is in C.

Let’s see some examples of arrays:

In [14]: from numpy import *

In [15]: array(range(10))

Out[15]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) # Note the way it is printed.

# The output is explicitly telling you that this is an array and not a list.

In [16]: A = array([[1,2,3],[4,5,6],[7,8,9]]); A

Out[16]:

array([[1, 2, 3],

[4, 5, 6],

[7, 8, 9]])

In [17]: A * 10 # Multiplication with a scalar

Out[17]:

array([[10, 20, 30],

[40, 50, 60],

[70, 80, 90]])

In [18]: A * A # Element by element multiplication

Out[18]:

array([[ 1, 4, 9],

[16, 25, 36],

[49, 64, 81]])

In [19]: A + 10 # Addition with a scalar

Out[19]:

array([[11, 12, 13],

[14, 15, 16],

[17, 18, 19]])

In [20]: A + A # Addition with a vector

Out[20]:

array([[ 2, 4, 6],

[ 8, 10, 12],

[14, 16, 18]])

In [21]: ones(10) # Try “ones?” and see what does it do

Out[21]: array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1.])

In [10]: B = array([ones(3) for i in range(3)]); B

Out[10]:

4

array([[1., 1., 1.],

[1., 1., 1.],

[1., 1., 1.]])

In [22]: dot(A,B)

Out[22]:

array([[ 6., 6., 6.],

[15., 15., 15.],

[24., 24., 24.]])

Now, try out the same operations with lists and see what happens. Here is an example:

In [23]: a = [list(i) for i in A]; a

Out[23]: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

In [24]: a + a

Out[24]: [[1, 2, 3], [4, 5, 6], [7, 8, 9], [1, 2, 3], [4, 5, 6], [7, 8, 9]]

arrays will give you what a mathematician would expect, while list is more for a general

programming use. Arrays are quite fast compared to lists. Here are some important facts

about arrays:

• Array elements are all of one type, unlike lists. This is precisely to improve the speed of

computation.

• An array of integers is different from an array of reals or an array of doubles. So you can

also use the second argument to create an array of the correct type.

Eg:

x=array([[1,2],[3,4]], dtype=complex)

• Arrays are stored row wize by default. This can be changed by setting some arguments

in numpy functions. This storage is consistent with C.

• The size and shape methods give information about arrays. In above examples,

x.size # returns 4

x.shape # returns (2, 2)

len(x) # returns 2

size gives the number of elements in the array. shape gives the dimensions while len

gives only the number of rows.

• Arrays can be more than two dimensional. This is a big advantage over Matlab and

its tribe. Scilab has hypermatrices, but those are slow. Here arrays are intrinsically

multi-dimensional

5

• The dot operator does tensor contraction. The sum is over the last dimension of the first

argument and the first dimension of the second argument. In the case of matrices and

vectors, this is exactly matrix multiplication.

Note that numpy also has a matrix data type. But this is not recommended to use. In fact,

the community is planning to remove it in future.

4.2 where()

Sometimes we want to know the indices in a matrix that satisfy some condition. The method

to do that in Python is to use the where command. To find the even elements in the above

matrix we can do:

In [25]: A=array([[1,2,3],[4,5,6],[7,8,9]]);A

Out[25]:

array([[1, 2, 3],

[4, 5, 6],

[7, 8, 9]])

In [26]: coords = where(A%2==0) # Returns the coords of even elements

In [27]: coords # This is a tuple

Out[27]: (array([0, 1, 1, 2]), array([1, 0, 2, 1]))

In [28]: i, j = where(A%2==0)

In [29]: B=array([[6,6,6],[4,4,4],[2,2,2]])

In [30]: i, j = where((A>3)&(B<5)>0)

What does the last line here do? Break it down to pieces and understand what each piece

does. Can you replace the & with and? Why not?

Here is another peculiar thing about arrays.

In [31]: A

Out[31]:

array([[1, 2, 3],

[4, 5, 6],

[7, 8, 9]])

In [32]: i, j = where(A%2==0); i

Out[32]: array([0, 1, 1, 2])

In [33]: j

Out[33]: array([1, 0, 2, 1])

6

In [34]: A[i, j]

Out[34]: array([2, 4, 6, 8])

Notice what is happening in the last command. Can you do the same with lists?

4.2.1 Homework

• Explain what is happening in the last command here to your TA.

In [35]: A

Out[35]:

array([[1, 2, 3],

[4, 5, 6],

[7, 8, 9]])

In [36]: A[i][j]

Out[36]:

array([[4, 5, 6],

[1, 2, 3],

[4, 5, 6],

[4, 5, 6]])

• Generate an array (of shape (3,3)) of random numbers.

5 Solving Linear Equations

Solving linear equations are quite simple using numpy. Let’s try and solve:

3×0 + x1 = 9 (1)

x0 + 2×1 = 8 (2)

The above equation can be written as:

Ax = b (3)

Where,

A =

3 1

1 2

(4)

x =

x1

x2

(5)

b =

9

8

(6)

7

All you need to do is to create the matrices:

In [37]: import numpy as np

In [38]: A = np.array([[3,1], [1,2]]); A

Out[38]:

array([[3, 1],

[1, 2]])

In [39]: b = np.array([9,8]); b

Out[39]: array([9, 8])

In [40]: x = np.linalg.solve(A, b); x

Out[40]: array([2., 3.])

The numpy.linalg.solve() solved the set of equations for us.

Note that the first line is same as “import numpy”. But “as np” here allows you to write

np.array() instead of numpy.array(). This is simply to save some typing efforts.

6 Spice – Part 2

In the last assignment, we read a netlist file and parsed it. In this assignment, we will solve

the circuits. We have n node voltages (say Vi

; i = 0 to n − 1) and k currents (say Iij ) (the

currents through the k voltage sources). The system of equations are:

1. n KCL equations at the n nodes.

2. k equations of the type Vi − Vj = ij for nodes connected by voltage sources.

As long as a loop consisting purely of voltage sources or a node connected purely to current

sources does not exist, a unique solution is possible. Write the equations in the form

A · V~ + B ·

~I = ~b (7)

The node equations take the form

X Vi − Vj

Zij

+

XIij = −

XIij (8)

where the first sum is over the passive branches, the second sum over voltage sources and the

third sum is over current sources.

The auxiliary equations take the form

Vi − Vj = Eij (9)

The dependent sources have similar equations that you can easily derive for yourself.

8

• Explain how a loop consisting purely of voltage sources will result in the system of

equations becoming inconsistent.

• Explain how a node connected purely to current sources will result in the system of

equations becoming inconsistent.

Now, write the equation 7 in the following form:

Mx = b (10)

i.e.,

a11 … a1n b11 … b1k

… … … … … …

an1 … ann bn1 … bnk

… … … 0 0 0

… … … 0 0 0

… … … 0 0 0

V1

…

Vn

I1

…

Ik

=

I1

…

In

E1

…

Ek

(11)

This matrix needs to be solved to obtain currents and voltages.

Note that this can be readily solved using the method specified in section 5.

7 Assignment

1. Parse the netlist and create list(s) of different components. You may define class(es)

for components and create objects for each component to store them nicely. Component

name, connected nodes, value etc… can be the features of this(these) class(es). Remember

that list elements can be class objects too.

2. Create a table of distinct nodes present in the circuit. Assign numbers to the nodes so

that they correspond to the rows (columns) of the incidence matrix. You may use a

dictionary for doing this. You could then store the node number as the value with the

node name as the key.

Note: You can assume that ground node is always named as GND. The ground node will

add an extra equation:

Vk = 0 (12)

where k is the node number of GND. You may keep it as V0 always.

3. Construct matrices M and b using numpy arrays.

4. Solve the equation Mx = b.

5. Print all the unknowns. (All node volatges and current through voltage sources)

6. Solve the following circuits with your program:

9

(a) A simple resistive circuit (use RL = 1, 10 and 100Ω)

n0

− 10V +

n1

R1 = 2Ω n2

R2 = 3Ω

R3 = 5Ω n3

RL

GND

Figure 1: Resistive circuit

7. Can you use the same techniques to solve for ac circuits? We only have to interpret the

impedance as complex numbers and the solution will follow. We will only work with

circuits with single frequency at steady state.

Till now, we only had a single command in the netlist (.circuit … .end). We now

allow a new command .ac.

.ac V… frequency

This is a single line command. It will appear after the .circuit … .end block and

specify the frequency of a voltage source.

We will also modify the way voltage source and current values are given. We will use the

following representations:

V… n1 n2 ac Vp−p phase # ac voltage source

V… n1 n2 dc v # dc voltage source

Similar representations will follow for current sources.

Solve the following circuits:

(a) A band-pass filter for the current in the resistor

10

n1

5 Vp−p

0 Vof fset

1 kHz

1.0 F

n2

1.0 kΩ n3

1.0 µH

n0 GND

Figure 2: Band-pass filter

(b) A low-pass filter, for the voltage across the load resistor

Vp−p = 1

0 Vof fset

1 kHz

n1

4.5 kΩ n2

80.96 µH n3

80.96 µH n4

4 kΩ

n0

2.485 pF

GND

Figure 3: Low-pass filter

Submit a single program on Moodle. It should be able to solve ac as well as dc circuits.

11