Notes Python
Notes Python
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
Out[8]:
17, 18, 19])
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 1,
2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
array([ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32,
Out[9]:
34, 36, 38])
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 1,
2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 1, 2, 3,
4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
array([ 0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48,
Out[10]:
51, 54, 57])
array([ 0. , 3.7, 7.4, 11.1, 14.8, 18.5, 22.2, 25.9, 29.6, 33.3, 37. ,
Out[11]:
40.7, 44.4, 48.1, 51.8, 55.5, 59.2, 62.9, 66.6, 70.3])
In [12]: type(a)
list
Out[12]:
In [13]: type(b)
numpy.ndarray
Out[13]:
149 µs ± 0 ns per loop (mean ± std. dev. of 1 run, 10,000 loops each)
779 ns ± 0 ns per loop (mean ± std. dev. of 1 run, 1,000,000 loops each)
print (σ_x)
[[0. 1.]
[1. 0.]]
array([[[1., 1.],
Out[19]:
[1., 1.]],
[[1., 1.],
[1., 1.]]])
NOTE 1: One can use np.ndarray to create an array. Here, all the elements are initialised
with some random number. However, this is not recommended.
NOTE 2: The use of the np.matrix subclass is not recommended as it may get
deprecated in the future. NumPy forums recommend only the use of np.ndarray class
for all matrices.
Shape and dimension of an array
In [21]: mat_1.ndim # returns the dimension of the array
3
Out[21]:
In [22]: mat_1.shape # returns a tuple with the shape or all the dimensions of the ar
(2, 2, 2)
Out[22]:
[0 1 2 3 4 5 6 7 8 9]
(10,)
Out[23]:
[[0 1 2 3 4]
[5 6 7 8 9]]
(2, 5)
Out[24]:
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Input In [25], in <cell line: 1>()
----> 1 print(num_list.reshape(4,2))
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]
[12 13 14 15]]
In [27]: mat[0,2] # the containers for accessing element 2D arrays (row index, column
2
Out[27]:
[[[ 0 1 2 3]
[ 4 5 6 7]]
[[ 8 9 10 11]
[12 13 14 15]]]
In [29]: tens[0,1,3] # accesses 1st, 2nd, 4th element from the tensor
7
Out[29]:
In [30]: tens[0,2,3] # calls 1st, 3rd, 4th element from the tensor, but 3rd element
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
Input In [30], in <cell line: 1>()
----> 1 tens[0,2,3]
Matrix A is:
[[4 3 0 1]
[4 3 4 2]
[4 0 2 4]]
Matrix A is:
[1 4 1]
Matrix A is:
[[ 1 7 8 8]
[17 13 14 12]]
Matrix A is:
[[0.960704 0.31006085]
[0.00543051 0.33408133]
[0.26585241 0.53001972]]
Matrix A is:
[[-0.25045079 -2.08384439 -1.07010669]
[-0.51716863 0.69883823 -1.46131635]
[ 0.47136285 -1.33294674 1.46969341]]
Matrix B is:
[[3 1 1 2]
[1 0 1 4]
[4 3 4 0]]
Element-wise multiplication:
[[ 0 6 0 2]
[12 3 2 4]
[ 2 12 0 4]]
Element-wise exponentiation:
[[ 0 4 0 1]
[ 9 1 1 1]
[ 1 16 0 4]]
Matrix multiplication:
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Input In [38], in <cell line: 6>()
4 print ('Matrix multiplication:\n') #
5 print ()
----> 6 A @ B
Matrix B is:
[[1 1 1 1]
[1 0 0 0]
[1 1 1 1]
[0 1 1 1]]
Matrix multiplication:
[[1 1 1 1]
[1 0 0 0]
[1 0 0 0]
[2 3 3 3]]
Matrix A is:
[[0 0 0 1]
[0 1 1 1]
[1 1 0 0]
[1 0 0 0]]
Matrix B is:
[[0]
[0]
[1]
[0]]
Matrix multiplication:
[[0]
[1]
[0]
[0]]
In [41]: A = np.random.randint(3,size=(4))
B = np.random.randint(3,size=(4))
print ('Matrix A is:\n', A)
print ()
print ('Matrix B is:\n', B)
print ()
print ('Inner product:\n', np.dot(A,B)) # Inner product of two 1D arrays.
# For 2D this gives matrix multipli
Matrix A is:
[0 0 2 0]
Matrix B is:
[2 0 1 2]
Inner product:
2
numbers between [0,10). Reshape them to 1D vectors and add them. Now reshape
them back to 3 × 6 and compare the sum with the direct sum A + B.
1. Consider the set of equations:
′
x = 15x + 3y + 5z
′
y = −5x + 2z
′
z = 8x + 13y + 5z
1. Using NumPy, show that Pauli matrices are indeed unitary. Also, check if sum of two
Pauli matrices are indeed unitary.
1. Check the np.kron(A,B) function. This creates a tensor (Kronecker) product of two
matrices.
https://en.wikipedia.org/wiki/Kronecker_product
Use NumPy matrix calculations to verify the np.kron(A,B) command.
158 µs ± 2.85 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
i = 0
while i < 10000:
i += 1
297 µs ± 0 ns per loop (mean ± std. dev. of 1 run, 100 loops each)
In [3]: %%timeit -r1 -n100 # Again, -rx mean run x times, and -ny means y loops for
# If you do not specify, then r is 7 and y is 1000000 by
k = 0
for i in range(10000):
k += 1
636 µs ± 0 ns per loop (mean ± std. dev. of 1 run, 100 loops each)
In [5]: %%timeit
factorial_loop(5)
253 ns ± 10.5 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
In [6]: %%timeit
factorial_rec(5)
288 ns ± 8.84 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
start = time.time() # gives the wall-time (total time taken by I/O and proce
# start = time.process_time() # gives the process-time (total time taken by
factorial_loop(1500)
end = time.time()
# end = time.process_time()
print ("Time taken to run the block in seconds (default):",end-start)
start = time.time()
# start = time.process_time()
factorial_rec(1500)
end = time.time()
# end = time.process_time()
1. Copying an array the notion of assignment, shallow copy and deep copy
−
simple_list = list([1,2,10,34,'man','cat',[9,1,2],['women','baby','dog']])
print (simple_list)
[1, 2, 10, 34, 'man', 'cat', [9, 1, 2], ['women', 'baby', 'dog']]
print (dup_list)
print (simple_list,'\n')
print (id(dup_list)) # id gives you an integer associated with an object -- typically a memory location
print (id(simple_list)) # both lists have the same id
[1, 2, 10, 34, 'human', 'cat', [9, 1, 2], ['women', 'baby', 'dog']]
[1, 2, 10, 34, 'human', 'cat', [9, 1, 2], ['women', 'baby', 'dog']]
4381379264
4381379264
print (slice_list)
print (simple_list,'\n')
print (slice_list)
print (dup_list)
print (simple_list,'\n') # So changes in first index is independent -- but second index remains same
# This is called a shallow copy
4381774016
slice_copy[2] = 102
slice_copy[5][1] = 'teen'
slice_deepcopy[5][1] = 'adult'
print (simple_list)
print (slice_copy)
print (slice_deepcopy) # So a deep copy creates an independent copy at all levels of a list or an array
[1, 2, 10, 34, 'man', 'cat', [9, 1, 2], ['women', 'teen', 'dog']]
[10, 34, 102, 'cat', [9, 1, 2], ['women', 'teen', 'dog']]
[10, 34, 'man', 'cat', [9, 1, 2], ['women', 'adult', 'dog']]
The same thing holds for NumPy arrays, albeit with some subtle differences
One can use the np.copy function
In [5]: simple_array = np.array([1,2,10,34,'man','cat',[9,1,2],['women','baby','dog']],dtype=object)
# create an array from the list; we use dtype = object
# this ensures different objects are listed in an array including a nested list
print (simple_array)
slice_array = simple_array[3:]
print (slice_array,'\n')
slice_array[1] = 'human'
print (slice_array)
print (simple_array,'\n') # Slice here is an assignment and not a shallow copy, in contrast to lists
slice_array_copy = cp.copy(simple_array[3:]) # You can also use np.copy (in NumPy) for shallow copy
slice_array_copy[1] = 'monkey'
print (slice_array_copy)
print (simple_array,'\n') # Slice here is an assignment and not a shallow copy
slice_array_copy[4][0] = 'queen'
print (slice_array_copy)
print (simple_array,'\n') # This is indeed a shallow copy
slice_array_deepcopy = np.copy(simple_array[3:]) # np.copy is a shallow copy, but works for simple arrays
slice_array_deepcopy[4][1] = 'naughty'
print (slice_array_deepcopy)
print (simple_array,'\n')
[1 2 10 34 'man' 'cat' list([9, 1, 2]) list(['women', 'baby', 'dog'])]
[34 'man' 'cat' list([9, 1, 2]) list(['women', 'baby', 'dog'])]
[[[ 1 2 3]
[ 4 5 6]
[ 7 800 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]]]
[ 1 2 3 4 5 6 7 800 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]
print (copy_array,'\n')
print (num_array,'\n')
[[[ 1 2 3]
[ 4 5 6]
[ 7 800 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]]]
[[[ 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]]]
[[ 4 5 6]
[13 14 15]
[22 23 24]
[31 32 33]]
[[ 2 5 8]
[11 14 17]
[20 23 26]
[29 32 35]]
[[ 4 5 6]
[13 14 15]]
[[ 4 5]
[13 14]
[22 23]
[31 32]]
In the last notes, we looked at how general NumPy arrays can be created. Here we discuss a few more options to create, split and
stack arrays.
For more read: https://numpy.org/doc/stable/user/basics.creation.html
In [10]: Mat = np.empty([3,3])
for i in range(3):
for j in range(3):
Mat[i,j] = i + j
print (Mat)
[[0. 1. 2.]
[1. 2. 3.]
[2. 3. 4.]]
while i < 3:
while j < 3:
Mat[i,j] = i + j
j += 1
i += 1
print (Mat)
[[0. 1. 2.]
[1. 2. 3.]
[2. 3. 4.]]
In [12]: Mat = np.array([[i+j for i in range(3)] for j in range (3)],dtype=float) # np.empty by default is float
print (Mat)
[[0. 1. 2.]
[1. 2. 3.]
[2. 3. 4.]]
print (np.concatenate((Mat_split[0],Mat_split[-1])),'\n')
print (np.stack((Mat_split[0],Mat_split[-1]),axis=0),'\n')
print (np.row_stack((Mat_split[0],Mat_split[-1])),'\n')
print (np.vstack((Mat_split[0],Mat_split[-1])),'\n')
print (np.stack((Mat_split[0],Mat_split[-1]),axis=1),'\n')
print (np.column_stack((Mat_split[0],Mat_split[-1])),'\n')
[0 1 2 3]
[16 17 18 19]
[ 0 1 2 3 16 17 18 19]
[[ 0 1 2 3]
[16 17 18 19]]
[[ 0 1 2 3]
[16 17 18 19]]
[[ 0 1 2 3]
[16 17 18 19]]
[[ 0 16]
[ 1 17]
[ 2 18]
[ 3 19]]
[[ 0 16]
[ 1 17]
[ 2 18]
[ 3 19]]
[ 0 1 2 3 16 17 18 19]
In [15]: x_space = np.linspace(0,10,101) # can be used to create fractional steps; such as an x-axis in a plot
x_space
array([ 0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1. ,
Out[15]:
1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2. , 2.1,
2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 3. , 3.1, 3.2,
3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 4. , 4.1, 4.2, 4.3,
4.4, 4.5, 4.6, 4.7, 4.8, 4.9, 5. , 5.1, 5.2, 5.3, 5.4,
5.5, 5.6, 5.7, 5.8, 5.9, 6. , 6.1, 6.2, 6.3, 6.4, 6.5,
6.6, 6.7, 6.8, 6.9, 7. , 7.1, 7.2, 7.3, 7.4, 7.5, 7.6,
7.7, 7.8, 7.9, 8. , 8.1, 8.2, 8.3, 8.4, 8.5, 8.6, 8.7,
8.8, 8.9, 9. , 9.1, 9.2, 9.3, 9.4, 9.5, 9.6, 9.7, 9.8,
9.9, 10. ])
In [17]: print(np.cos(x_space[:15]),'\n')
print(np.exp(x_space[:15]),'\n')
print(np.pi) # Defining pi
[1. 0.99500417 0.98006658 0.95533649 0.92106099 0.87758256
0.82533561 0.76484219 0.69670671 0.62160997 0.54030231 0.45359612
0.36235775 0.26749883 0.16996714]
3.141592653589793
∙ Transpose of a matrix
In [18]: mat = copy_array[1,:,:2] # The slice gives you a 3 x 2 array
print (mat,'\n')
print (np.transpose(mat))
[[10 11]
[13 14]
[16 17]]
[[10 13 16]
[11 14 17]]
∙ Flatten, ravel
In [19]: print (mat) # Is a 3 x 2 complex matrix
print ()
print (mat.flatten()) # Flattens it to a 1D array
print ()
print (np.ravel(mat)) # Unravels it to a 1D array; same as flatten
[[10 11]
[13 14]
[16 17]]
[10 11 13 14 16 17]
[10 11 13 14 16 17]
∙ Trace of a matrix
In [20]: mat = copy_array[1,:,:] # The slice gives you a 3 x 2 array
print (mat)
print ()
np.trace(mat)
[[10 11 12]
[13 14 15]
[16 17 18]]
42
Out[20]:
∙ Diagonal of a matrix
In [21]: np.diagonal(mat) # Returns a 1D array of the diagonal elements
∙ Conjugate of a matrix
In [22]: comp_mat = np.random.rand(3,3) + 1j*np.random.rand(3,3) # A random complex matrix
print (comp_mat,'\n')
print (np.conjugate(comp_mat)) # Conjugate of a matrix
∙ np.floor
In [23]: comp_mat = np.random.randint(5,size=[3,3]) + np.random.rand(3,3)
print (comp_mat,'\n')
print (np.floor(comp_mat)) # The floor of the scalar x is the largest integer i, such that i <= x
[[1. 2. 2.]
[0. 3. 2.]
[1. 0. 4.]]
A = np.random.randint(5,size=(3, 3)) # Random array with shape (3,3) filled with integer elements from 0 to 5
print ('Matrix A is:\n', A,'\n')
Matrix A is:
[[0 0 2]
[1 3 0]
[4 3 0]]
-17.999999999999996
Out[24]:
e_val, e_vec = la.eig(B) # Compute the eigenvalues and right eigenvectors of a square array
print (e_val,'\n')
print (e_vec)
Matrix A is:
[[3 1 4]
[3 1 1]
[1 2 3]]
A_inv = la.inv(A) # Compute the eigenvalues and right eigenvectors of a square array
print (A_inv) # Compute the eigenvalues and right eigenvectors of a square array
Matrix A is:
[[0 1 3]
[3 4 3]
[1 0 1]]
Matrix exponentiation − A
n
In [27]: A = np.random.randint(5,size=(3, 3)) # Random array with shape (3,3) filled with integer elements from 0 to 5
print ('Matrix A is:\n', A,'\n')
A_pow3 - A @ A @ A
Matrix A is:
[[3 0 2]
[1 4 1]
[2 2 4]]
array([[0, 0, 0],
Out[27]:
[0, 0, 0],
[0, 0, 0]])
x = np.arange(100)
plt.plot(x,x**2,'r--') # Plotting the quadratic x^2 with x; r-- is for red with dashed lines
plt.plot(x,100*x,'k-') # Plotting the linear 100x with x; k- is for black with straight lines
plt.show()
In [29]: plt.semilogy(x,x**2,'r--') # Plotting the quadratic x^2 with x plotted in the logscale
[<matplotlib.lines.Line2D at 0x10a23d2d0>]
Out[29]:
In [30]: x = np.linspace(0.0,100,1000)
def func(x):
return 5*np.sin(x) + x
plt.plot(x,func(x),'g:')
[<matplotlib.lines.Line2D at 0x10a0198d0>]
Out[30]:
In [31]: A = np.random.randint(5,size=(2, 20)) # Random array filled with integer elements between [0,5)
plt.scatter(A[0,:],A[1,:])
<matplotlib.collections.PathCollection at 0x10a36eb10>
Out[31]:
In [32]: A = np.random.rand(2,100) # Random array with uniform random numbers between [0,1)
[<matplotlib.lines.Line2D at 0x10a441650>]
Out[32]:
In [33]: A = np.random.rand(50000,1) # Random 1D array with uniform random numbers between [0,1)
</br>
Create the quantum operator: H = σy ⊗ I + I ⊗ σy , where is the 2D identity matrix. Find the eigenvalues and eigenvectors of .
I H
Show that D = P
−1
, where is the diagonal matrix containing the eigenvalues of and is an invertible matrix formed by the
HP D H P
eigenvectors. </br>
Newton-Raphson method to find the root of a function
The root of a function is defined as
f (x) , such that
x = α . The Newton-Raphson method helps find the root of a continuous
f (α) = 0
function in an iterative manner. Starting with a guess for the root, it predicts a better guess , where
xn xn+1
f (xn )
xn+1 = xn − .
′
f (xn )
Here, ′
f (x) is the numerical derivate of the function f (x) , given by
df (x) f (x + h) − f (x)
′
f (x) ≡ = lim .
dx h→0 h
Find a function, that can be iteratively find the root of the function, , using the Newton-Raphson method
f (x) = sin(x) + 3 cos(x)
output of the function should be a tuple containing the approximate root of and the number of iterations needed to find it.
f (x) n
Hint: If the function is unable to find the root after a fixed number of iterations than you should restart it with a different initial
guess.
</br>
Using matplotlib, plot the function f (x) = sin(x) + 3 cos(x), in the range . Use atleast 1000 plot points. Compare this
x ∈ [0, 5π]
A = np.random.randint(5,size=(3, 3)) # Random array with shape (3,3) filled with integer elements from 0 to 5
print ('Matrix A is:\n', A,'\n')
e_val, e_vec = linalg.eig(A) # Compute the eigenvalues and right eigenvectors of a square array
print (e_val)
print ()
print (e_vec)
Matrix A is:
[[4 0 3]
[1 0 0]
[3 4 1]]
Matrix exponentiation
e , where is a square matrix
A
A
B = np.zeros((2, 2))
print (linalg.expm(B))
[[1. 0.]
[0. 1.]]
2. Numerical integration
Using the integrate package in SciPy
x=1
2
I = ∫ ax + bx dx
x=0
scipy.integrate.quad(func, a, b)
In [4]: from scipy import integrate
a,b = 1,2
integrate.quad(I,0,1,args=(a,b)) # Returns the integration with possible upper bound on the error
(2.333333333333333, 2.5905203907920317e-14)
Out[4]:
def f(y,x): # Note the order of x and y, while defining the function
return x*y**2
(0.6666666666666667, 7.401486830834377e-15)
Out[5]:
x=2 y=cos(x)
I = ∫ ∫ dy dx
x=0 y=sin(x)
integrate.dblquad(f, 0, np.pi/4, np.sin, np.cos) # Note that limits of y can be two functions of x
(0.41421356237309503, 1.1083280054755938e-14)
Out[6]:
Trapezium or trapezoidal rule
https://en.wikipedia.org/wiki/Trapezoidal_rule
To integrate I = ∫
x1
x0
, the idea is to slice the function into several trapeziums and calculate the area.
f (x) dx
print (int_sum)
333.33350033383414
V N
I ≈ ∑ f (xi ) ≡ V × ⟨f (x)⟩.
i=1
N
Let us consider, f (x) = x
2
between x = 0 and . Here,
x = 10 .
v = 10 − 0 = 10
In [27]: V = 10 - 0
x = np.random.uniform(0,10,N) # we choose N points between 0 and 10
I = V/N*sum(f(x))
print (integrate.quad(f,0,10))
335.5959563911658
(333.33333333333337, 3.700743415417189e-12)
0 1 n
F (x, y, y , y , … , y ) = 0,
n 0 1 n−1
y = g(x, y, y , y , … , y ),
where y = [y0 , y1 , y2 , … , ym ] .
Consider the following:
dy
dt
= f (t, y) , given y(t0 ) = y0 , where y is a function, either a scalar or a vector.
1) dy
dt
= y
def func(t,y):
dydt = y
return dydt
t = np.linspace(0,5,10)
y_out = solve_ivp(func, t_span = [0,5], y0 = np.array([1.0]), method='RK45', t_eval=t)
print (y_out.y[0]) # So y_out.t contains t, and y_out.y contains y(t). Careful of the containers.
In [2]: y_out.y
In [4]: plt.plot(y_out.t,y_out.y[0])
[<matplotlib.lines.Line2D at 0x298d7d950>]
Out[4]:
2) dy
dt
= sin(5t) + 0.1t
t = np.linspace(0,5,100)
y_out = solve_ivp(func, t_span = [0,5], y0 = np.array([1.0]), method='LSODA', t_eval=t)
y_out
message: The solver successfully reached the end of the integration interval.
Out[6]:
success: True
status: 0
t: [ 0.000e+00 5.051e-02 ... 4.949e+00 5.000e+00]
y: [[ 1.000e+00 1.013e+00 ... 1.798e+00 1.794e+00]]
sol: None
t_events: None
y_events: None
nfev: 115
njev: 0
nlu: 0
In [2]: plt.plot(y_out.t,y_out.y[0])
[<matplotlib.lines.Line2D at 0x7f1b019773d0>]
Out[2]:
3) Solve the following ODE:
dx
2 = −x(t) + u(t)
dt
dy
5 = −y(t) + x(t)
dt
In [3]: t = np.linspace(0,40,400)
x0 = np.array([0.0,0.0])
y_out = solve_ivp(func, t_span = [0,40], y0 = x0, method='BDF', t_eval=t)
In [9]: x=y_out.y[0]
y=y_out.y[1]
In [3]: plt.plot(y_out.t,y_out.y[0],'r')
plt.plot(y_out.t,y_out.y[1],'b')
plt.plot(t,[u(i) for i in t],'k--')
[<matplotlib.lines.Line2D at 0x7f1b018f3910>]
Out[3]:
4) Solve the following ODE:
dx1
= x2 (t)
dt
dx2
= 2x1 (t) − x2 (t)
dt
x1 (0) = 1; x2 (0) = 2.
dt
= A × x, where A = (
0
2
1
−1
) , and x = (
x1
x2
). The initial condition is x(0) = (
1
2
).
In [ ]: A = np.array([[0,1],[2,-1]])
def func(t,x):
x1 = A@x
return x1
t = np.linspace(0,5,100)
x0 = np.array([1.0,2.0])
y_out = solve_ivp(func, t_span = [0,5], y0 = x0, method='LSODA', t_eval=t)
In [ ]: plt.plot(y_out.t,y_out.y[0])
dy
= x(ρ − z) − y
dt
dz
= xy − βz
dt
Convert the second order differential equation to a set of ODEs and solve: ,
2
d y dy
+ 3 + ty = cos(t)
2
dt dt
where dy
dt
′
= y (0) = −2 and . Plot the solutions of the ODEs for some time range.
y(0) = 2
Solve the above equation using solve_ivp, and compare the solutions with , where
⎛ ⎞
At
x(t) = e x(0) x(0) = ⎜ −1 ⎟ .
⎝ ⎠
0
Solving ordinary differential equations
In this class we look at the Runge-Kutta method and some problems involving ODEs.
Runge-Kutta (RK) methods
RK methods are a family of iterative method for solving ordinary differential equations. It's simplest form, the first order RK, is nothing but
the Euler method, which we have encountered earlier. In general, it is the fourth-order Runge Kutta method (aka RK4 ), which is widely
used and is often eponymous while discussing these methods.
The RK4 can be described as: dy
, where is some unknown function (scalar or vector) that we want to find, with some initial
= f (x, y) y
values, and .
dx
x = x0 y(x0 ) = y0
Now, for some , the function at different values of can be iteratively estimated using,
h > 0 y x
1
yn+1 = yn + (k1 + 2k2 + 2k3 + k4 ) h,
6
xn+1 = xn + h,
where n = 0, 1, 2, 3, … , and
k1 = f (xn , yn ),
k1 h
k2 = f (xn + h , yn + ),
2 2
k2 h
k3 = f (xn + h , yn + ), and
2 2
k4 = f (xn + hk3 , yn + h)
Note: If ki = k1 = f (xn , yn ), ∀i, we get back the first-order RK or the Euler method. Use the RK4 method above to solve the following
problem:
dy
= y sin(t), where 2
. Plot the function for different values of and also the exact solution for
y(0) = 1 y h . Compare with
t ∈ [0, 5]
dx
dy
= x
2
+ y
2
, where x(1) = 1.2 . Find, for x y ∈ [1.0, 10.0] with h = 0.01 . Compare with "solve_ivp" using the LSODA solver.
Quantum dynamics
A two-level state (say a spin) in quantum mechanics can be written as:
α
|ψ⟩ = α|1⟩ + β|0⟩ = [ ] , where α, β ∈ C.
β
Here, and , are the basis states and can refer to the energy level or eigenstates of any observable in the two-dimensional Hilbert
|0⟩ |1⟩
space.
Moreover, operators acting on the systems are given by matrices such as the Pauli matrices,
0 1 0 −i 1 0
σx = ( ) , σy = ( ) , and σz = ( ).
1 0 i 0 0 −1
Consider the Hamiltonian for a system of a quantum spin and a photon, given as:
ωs 0 0 σx ± iσy
† + † − ±
^ a +
H = ωc a ^σ
σz + g(a ^ σ
+ a ^ = (
), where a ) , and σ = .
2 1 0 2
Here, ^ (a
a ^ )
†
is the photon annihilation (creation) operator, whereas act on the spin. σ
±
Now, for any Hamiltonian, , the time evolution of a quantum state is given by the equation:
H
d|ψ⟩
= H|ψ⟩ . If the spin-photon system is
initially in the state
dt
1 0
|ψ(0)⟩ = [ ] ⊗ [ ] , (1)
0 1
spin photon
where spin state is in the up/excited and photon state is in vacuum.
The Hamiltonian parameters are given by , and
ωc = ωs = 1 g/ωc = 10, solve the dynamics of the system and plot the expectation value
−3
of , which gives us the average number of photons in the state, for increasing time .
†
^ a⟩
⟨a t
vectors ∗
|ψ (t)⟩ and .
(I ⊗ B)|ψ(t)⟩
Basic epidemiology
Let us consider a virus infection that spreads and can develop into a deadly disease X. Without treatment with antiviral drugs, survival time
after infection and onset of disease X is about 9 to 11 month, depending on a number of factors.
The spread of the viral infection in a body with an initial infection is approximated with balance equations on the number of healthy cells ( H
dH
= r1 − r2 H − r3 H V
dt
dI
= r3 H V − r4 I
dt
dV
= −r3 H V − r5 V + r6 I ,
dt
infected cells, r4 = 0.5 is the death rate of infected cells, is the death rate of virus, and
r5 = 5 r6 = 100, is the production of virus by
infected cells. All rates are per month.
Plot the healthy cell, infected cell, and the virus count over the course of 15 months, if the initial counts are: H (0) = 10
6
, , and
I (0) = 0
V (0) = 100 . Please use the RK4 method derived in the first section.
In [ ]:
Python Dictionaries
So, far we have seen three in-built data-types in Python that can be used to store data, viz lists, tuples and sets. Each have their own
characteristics.
Today, we look at the fourth type. A Python dictionary. A dictionary stores data values in key-data pairs. In latest Python version,
dictionaries are ordered, changeable and cannot have duplicate keys.
In [1]: import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
In [2]: new_dict = {
"Name": ['Rahul','Sourav','Sachin'], # You can have any data type as values, i.e., np.array, string
"Debut": (1996,1992,1989),
"Century": np.array([48,38,100])
}
print(new_dict)
{'Name': ['Rahul', 'Sourav', 'Sachin'], 'Debut': (1996, 1992, 1989), 'Century': array([ 48, 38, 100])}
3
<class 'dict'>
<class 'tuple'>
<class 'numpy.ndarray'>
dict_keys(['Name', 'Debut', 'Century'])
Out[3]:
{'Name': ['Rahul', 'Ganguly', 'Sachin'], 'Debut': (1996, 1992, 1989), 'Century': array([ 48, 38, 100]), 'Wicket':
[5, 132, 200]}
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[4], line 2
1 # Adding new elements to each list
----> 2 new_dict["Debut"].append(1996)
In [28]: new_dict
In [29]: new_dict.items() # Gives you the key and value pair as a tuple
dict_items([('Name', ['Rahul', 'Ganguly', 'Sachin']), ('Debut', (1996, 1992, 1989)), ('Century', array([ 48, 38, 10
Out[29]:
0])), ('Wicket', [5, 132, 200])])
Saving data in Python
We look at ways in which data can be saved in Python. There are 3 types we will discuss 1) simple .txt for simple text format, 2) csv file for
data that can be written as a table (think MS Excel), and 3) using pickle, NumPy and json for saving objects, dictionaries or more
structured data.
Using txt
In [10]: ## Saving text data
for i in txtdata:
file.writelines([i,'\n']) # Writing text -- use "write" if you just have a single data
file.close()
ABCD
Using CSV
CSV stands for comma separated values. Works well with pandas.
In [69]: import csv
csvwriter.writerow(heads)
csvwriter.writerows(data)
In [30]: np.save('cricket.npy',new_dict,allow_pickle = True) # The pickle module implements binary protocols for
# serializing and de-serializing a Python object structure
print(new_dict["Name"])
['Dravid', 'Ganguly', 'Tendulkar']
{'Name': ['Rahul', 'Ganguly', 'Sachin'], 'Debut': (1996, 1992, 1989), 'Century': array([ 48, 38, 100]), 'Wicket':
[5, 132, 200]}
{'Name': ['Dravid', 'Ganguly', 'Tendulkar'], 'Debut': (1996, 1992, 1989), 'Century': array([ 48, 38, 100]), 'Wicke
t': [5, 132, 200]}
dy 1
= − (y(t) − x(t)),
dt 5
Create a dictionary containing the name of your five of your friends, their city of birth, hometown, and a fictional passport number.
Now write a function that can add a new friend's information. Add the Head as the new friend, {'HOD','Kolkata','Mumbai','XYZ789'}.
Saving Python dictionaries using json
What is json? JavaScript Object Notation (JSON) is a standard text-based format for representing structured data based on JavaScript
object syntax but applicable to other platforms. It is commonly used for transmitting data in various applications (e.g., sending some data
from the server to the client or vice versa).
using json
In [ ]: import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
import pickle
import json
new_dict
In [ ]: with open("dict.json", "w") as outfile: # Open a new json file, for writing "w"
json.dump(new_dict,outfile,indent=4) # Dump the new_dict dictionary to the open json file
In [ ]: new_dict
In [ ]: with open("dict.json", "w") as outfile: # Open a new json file, for writing "w"
json.dump(new_dict,outfile,indent=4) # Dump the new_dict dictionary to the open json file
In [ ]: json_dict["City"] = ['Bengaluru','Kolkata','Mumbai']
In [ ]: # json_dict
with open("dict.json","w") as loadfile:
json.dump(json_dict,loadfile,indent=4)
# def add_jsonfile(add_key,add_value,filename='dict.json'):
# with open(filename, "r+") as outfile: # Open a json file, for reading and writing "r+"
# dict_ = json.load(outfile)
# dict_[add_key] = add_value
# with open(filename, "w") as outfile: # Open the json file, only for writing "w" to overwrite the file
# json.dump(dict_,outfile,indent=4)
# add_jsonfile('City',['Bengaluru','Kolkata','Mumbai','Hyderabad'],'dict.json')
with open("student.json", "w") as outfile: # If I want to overwrite then always use "w"
json.dump(students,outfile,indent=2)
In [ ]: def append_jsonfile(add_key,add_value,filename='stud.json'):
with open(filename, "r+") as outfile: # Open the json file, for reading and writing "r+"
dict_ = json.load(outfile)
dict_[add_key].append(add_value)
with open(filename, "w") as outfile:
json.dump(dict_,outfile,indent=2)
In [ ]: add_val = {'Name':'Alex',
'Age': 21,
'Roll':'21ND005'}
append_jsonfile('2021',add_val,'student.json')
In [ ]: add_jsonfile('2020',[],'student.json')
In [ ]: new_student
Using pandas
Pandas is a Python library that provides various data structures and operations for manipulating numerical data. Built on top of the NumPy
library, Pandas is fast, productive and high performing.
https://www.geeksforgeeks.org/introduction-to-pandas-in-python/
In [ ]: import pandas as pd
list0 = (students['2022'][1])
print (list0,'\n')
In [ ]: print(list_2022[['Name','Age']])
In [ ]: large_data.keys()
In [ ]: print (large_data['city'])
In [ ]: print (large_data.loc[list(range(20,25)),['toss_winner','winner']])
In [ ]: count = 0
for i,j in large_data.iterrows():
if j['city'] == 'Kolkata':
print(j['id'],j['winner'])
count += 1
print()
print ("Games held in Kolkata: ",count)
In [ ]: # print (large_data.loc[[2008],['winner','player_of_match']])
import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
# Initial conditions
initial_conditions = [0, 0]
# Time span
t_span = (0, 40) # Adjust the time range as needed
1
plt.legend()
plt.show()
import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
2
def system(t, x):
return np.dot(A, x)
# Initial condition
x0 = np.array([1, 0, 0]) # You can change the initial condition as needed
# Time span
t_span = (0, 5) # Adjust the time range as needed
# Analytical solution
def analytical_solution(t):
eigenvalues, eigenvectors = np.linalg.eig(A)
matrix_exp = np.dot(eigenvectors, np.dot(np.diag(np.exp(eigenvalues * t)),␣
↪np.linalg.inv(eigenvectors)))
3
[14]: #Convert the second order differential equation to a set of ODEs
# y" + 3 y' + yt = cost ,
#where y'(0) = -2 and y(0) = 2 . Plot the solutions of the ODEs for some time␣
↪range.
import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
# Initial conditions
initial_conditions = [2, -2]
# Time span
4
t_span = (0, 50) # Adjust the time range as needed
#sigma , rho , beta = 5,4,1 You can take the time, . Plot vs , and vs . You can␣
↪use the initial state to be x = [1,1,1]
5
import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
# Parameters
sigma = 5
rho = 4
beta = 1
# Initial conditions
initial_conditions = [1, 1, 1]
# Time span
t_span = (0, 10) # Adjust the time range as needed
6
[18]: #Write a function that implements composite simpsons rule and integrates the␣
↪function f(x) = x^2 logx between x = 1 and x =10
import numpy as np
def f(x):
return x**2 * np.log(x)
Parameters:
- f: The function to be integrated.
- a: The lower limit of integration.
- b: The upper limit of integration.
- n: The number of subintervals.
Returns:
The approximate definite integral.
"""
h = (b - a) / n
7
x_values = np.linspace(a, b, n + 1)
return (h / 3) * integral
result = composite_simpsons_rule(f, a, b, n)
print("Approximate integral:", result)
#Compare the above results obtained using the function and trapezoidal method.
#Now, taking in both cases, show whether the Simpson's or trapezoidal rule␣
↪gives you a value closer to the inbuilt function.
import numpy as np
from scipy.integrate import quad
def f(x):
return x**2 * np.log(x)
return (h / 3) * integral
8
h = (b - a) / n
x_values = np.linspace(a, b, n + 1)
return h * integral
# Interval
a = 1
b = 10
# Number of subintervals
n = 1000 # Adjust as needed
# Print results
print("Composite Simpson's Rule result:", result_simpsons)
print("Trapezoidal Rule result:", result_trapezoidal)
print("SciPy quad function result:", result_quad)
import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
9
dvdt = -k * x
return [dxdt, dvdt]
# Time span
t_span = (0, 50)
10
[20]: #Now, consider two harmonic oscillators with displacements along the and axis,
#respectively. Using, for the two oscillators, use a ODE solver to find the
#displacement of the two oscillators along the x and yaxis.
#Using matplotlib, plot vs to create the Lissajous figures: a)k1 = 1 and k2 =␣
↪1, b)k1 =1
import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
# Time span
t_span = (0, 50)
initial_conditions = [1, 0, 1, 0]
11
[23]: #The working of a dye-based, single-mode microlaser can be modelled using the
##following equation of motion:
#dn/dt = -kn + NE(n+1)p
#dp/dt = -[Td+E(n+1)p]+Tu(1-p)
#where is the average number of photons inside the cavity, is total number of␣
↪dye
#and Td are the molecular pumping rate and molecular de-excitation rates
#Write a function that allows you to find the steady state photon number, ,
#such that at some time . Take time steps of 20 (in units of kappa), i.e.,␣
↪t_span =[0,20]
##. Moreover, the solution of the first time span can be taken as the initial␣
↪state of the second
#Note your function needs to check for steady state by ensuring that the photon␣
↪number
12
#between two time steps change (absolute relative value) by a value smaller␣
↪that 10^-2
import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
N = 10**8
E = 10**(-5)
Tu = 0.25*10**(-4)
Td = 0.25
time_spans = (0,20)
initial_conditions = [0, 0]
def find_difference_last_two(arr):
#print(len(arr))
if len(arr) >= 3:
last_element = arr[-1]
second_last_element = arr[-2]
difference = np.abs((last_element - second_last_element) /␣
↪second_last_element)
print(difference)
return difference
else:
return None
for io in range(1,100,1):
t_values = sol.t
13
n_values = sol.y[0]
p_values = sol.y[1]
result = find_difference_last_two(n_values)
plt.xlabel('Time')
plt.ylabel('Values')
plt.legend()
plt.show()
0.1303265633652936
Time span (0, 20) has not reached the steady state
0.0014174187624710543
Time span (0, 20, 0, 20) has reached the steady state
14
[24]: #In the above problem, change Tu/Td in the range , and by plotting the
#steady state photon number and the in the log scale, show that there is␣
↪driven}dissipative phase transition in the microlaser syst
import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
N = 10**8
E = 10**(-5)
Td = 0.25
steady_state_photon_numbers = []
for Tu in Tu_values:
def system_of_equations(t, variables):
n, p = variables
dndt = -n + N * E * (n + 1) * p
15
dpdt = -p * (Td + E * (n + 1)) + Tu * (1 - p)
return [dndt, dpdt]
n_values = sol.y[0]
steady_state_photon_numbers.append(n_values[0])
plt.semilogx(Tu_values, np.log10(steady_state_photon_numbers))
plt.xlabel('Tu (log scale)')
plt.ylabel('Steady-State Photon Number (log scale)')
plt.title('Steady-State Photon Number vs Tu (log scale)')
plt.grid(True)
plt.show()
16
[4]: #Solve the following differential equation
# dx/dt = x + 1
#dy/dt = (-1/5)*(y-x) where x(0) = y(0) = 0 and t = [0,10]
import numpy as np
from scipy.integrate import solve_ivp
import pickle
17
with open('solution.pkl', 'rb') as f:
loaded_data = pickle.load(f)
# Extract data
t = loaded_data['t']
x = loaded_data['x']
y = loaded_data['y']
# Create a plot
plt.figure(figsize=(8, 6))
plt.plot(t, x, label='x(t)')
plt.plot(t, y, label='y(t)')
plt.xlabel('Time (t)')
plt.ylabel('Values')
plt.legend()
plt.title('Plot of x(t) and y(t)')
plt.grid(True)
18
[5]: #Create a dictionary containing the name of your five of your friends, their␣
↪city of birth, hometown, and a fictional passport number.
#Now write a function that can add a new friend's information. Add the Head as␣
↪the new friend, {'HOD','Kolkata','Mumbai','XYZ789'}.
friends_info = {
'Friend1': {'City of Birth': 'City1', 'Hometown': 'Hometown1', 'Passport␣
↪Number': 'Passport1'},
19
[12]: #Consider the Lorenz system, a well-known example of a chaotic dynamical system.
↪ The Lorenz system is described by a set of first-order
#a. Simulate the Lorenz system for two different sets of parameter values:
# Set 1 : sigma = 10 rho = 24.184113703162021 and beta = 8/3
# Set 2 : sigma = 10 rho = 24.184113703162022 and beta = 8/3
# Provide separate plots for each set, showing the time evolution of the␣
↪state variables x, y, and z . Annotate each plot with the
#corresponding parameter values. Describe the observed behavior and differences␣
↪between the two sets
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import solve_ivp
# Initial conditions
initial_state = [1, 0, 0]
20
# Time span
t_span = (0, 100)
plt.tight_layout()
plt.show()
21
[14]: import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
# time points
t = np.linspace(0, 100, 5000)
# initial condition
state0 = [1.0, 1.0, 1.0]
# parameter values
params_set1 = (10, 24.184113703162021, 8/3)
params_set2 = (10, 24.184113703162022, 8/3)
# plot results
fig, axs = plt.subplots(2, figsize=(10, 10))
axs[0].plot(t, state_set1)
22
axs[0].set_title('Lorenz system evolution for sigma={}, rho={}, beta={}'.
↪format(*params_set1))
axs[1].plot(t, state_set2)
axs[1].set_title('Lorenz system evolution for sigma={}, rho={}, beta={}'.
↪format(*params_set2))
plt.tight_layout()
plt.show()
23
[15]: #The trajectory of a cannon shell, fired from a point in ground, is given by a␣
↪set of equations:
#dx/dt = vx
#dy/dt = vy
#dvx/dt = -bvvx
#dvy/dt = -g-bvvy
#starts with the launch of cannon ball from origin and ends when cannon ball␣
↪hits the ground. . Plot the range as a function of launch angles.
#Among these launch angles, find the angle that has the greatest range.
#Hint 1: While solving the ODE, take the time span to greater than 20 ( Why?)␣
↪\\ Hint 2: The solution will give the parts where y<0. You don't
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
# time points
t = np.linspace(0, 20, 1000)
# initial condition
state0 = [0, 0, 10, 10]
# parameter values
b = 0.0011
g = 9.8
ranges = []
for angle in angles:
state0[2] = 10*np.cos(angle) # vx
state0[3] = 10*np.sin(angle) # vy
state = odeint(cannonball, state0, t, args=(b, g))
24
# filter out parts where y < 0
state = state[state[:,1] >= 0]
# plot trajectory
plt.plot(state[:,0], state[:,1], label='angle = {:.2f}'.format(angle))
# calculate range
ranges.append(state[-1,0])
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.grid()
plt.show()
25
26
The angle that gives the greatest range is 0.70 rad
[17]: #Import/upload the "ipl_data.csv" file shared on Teams (please log on to Teams␣
↪on your
import pandas as pd
df = pd.read_csv(file_path)
# Filter the data for 2008 and count the number of times Asad Rauf or Aleem Dar␣
↪was the first umpire
27
count = df[(df['season'] == 2008) & ((df['umpire1'] == 'Asad Rauf') |␣
↪(df['umpire1'] == 'Aleem Dar'))].shape[0]
print(f"Asad Rauf or Aleem Dar was the first umpire in {count} matches in 2008.
↪")
Asad Rauf or Aleem Dar was the first umpire in 15 matches in 2008.
#to season, host city and winners (please check the exact key).
#ii) Count the number of wins and losses for Kolkata Knight Riders in 2010,␣
↪when they
import pandas as pd
import pandas as pd
df = pd.read_csv(file_path)
print(smaller_df)
kolkata_knight_riders_wins =␣
↪kolkata_knight_riders_2010[kolkata_knight_riders_2010['winner'] == 'Kolkata␣
↪Knight Riders']
kolkata_knight_riders_losses =␣
↪kolkata_knight_riders_2010[kolkata_knight_riders_2010['team1'] == 'Kolkata␣
↪Knight Riders']
28
season city winner
0 2008 Bangalore Kolkata Knight Riders
1 2008 Chandigarh Chennai Super Kings
2 2008 Delhi Delhi Daredevils
3 2008 Mumbai Royal Challengers Bangalore
4 2008 Kolkata Kolkata Knight Riders
.. … … …
572 2016 Raipur Royal Challengers Bangalore
573 2016 Bangalore Royal Challengers Bangalore
574 2016 Delhi Sunrisers Hyderabad
575 2016 Delhi Sunrisers Hyderabad
576 2016 Bangalore Sunrisers Hyderabad
[3]: #Say you are in a casino and are throwing a pair of dies.Using np.random.
↪randint write a single line of code that simulates a 10000 throws of the
import numpy as np
29
[7]: #Define a Matrix that takes a n*n matrix A as an input and outputs n*n-1 matrix␣
↪B containing only off diagonal elements.
import numpy as np
30
for x in x_values: n = 0 approx = f(x, n) exact = np.exp(x)
while relative_error(approx, exact) > tolerance:
n += 1
approx = f(x, n)
[11]: #The exponential function is given by the power series : exp(x) = lim k tends␣
↪infinity (1+x+x^2/2!+....+x^k/k!) where k! is the factorial. This can be␣
↪is done upto some finite integer n. Write a recursive function for f(x,n) .␣
↪number values of x : 0<x<20. Use while statement to run a loop over values␣
↪of x ,
# calculate the minimum n required for each x such that the relative␣
↪error between f(x,n) and exp(x) is less than 10^-10.
import numpy as np
import matplotlib.pyplot as plt
tolerance = 1e-10
x_values = range(1, 20)
n_values = []
for x in x_values:
n = 0
approx = f(x, n)
exact = np.exp(x)
n_values.append(n)
plt.plot(x_values, n_values)
31
plt.xlabel('x')
plt.ylabel('Minimum n for Accuracy < 10^-10')
plt.title('Minimum n vs. x')
plt.grid(True)
plt.show()
import numpy as np
import matplotlib.pyplot as plt
# Define parameters
alpha = 0.01
L = 1.0 # Length of the rod
Nx = 100 # Number of spatial grid points
Nt = 1000 # Number of time steps
dx = L / (Nx - 1)
32
dt = 0.001
# Perform time-stepping
for n in range(Nt):
U_new = U.copy()
for i in range(1, Nx - 1):
U_new[i] = U[i] + alpha * dt / dx**2 * (U[i + 1] - 2 * U[i] + U[i - 1])
U = U_new
U_history.append(U.copy())
33
34
[3]: pip install pandoc
[ ]:
35