Numerical Method Lab-Manual
Numerical Method Lab-Manual
METHODS
LAB MANUAL
First and foremost, we sincerely thank the esteemed faculty members of our
department for their unwavering support, guidance, and valuable insights
throughout the preparation of this manual. Their continuous encouragement has
greatly contributed to the overall quality of this work.
A special mention goes to Mr. Rabindra Baral, the editor, whose meticulous
work and attention to detail have significantly improved the content and
presentation of the manual.
This manual would not have been possible without the collective efforts of all
the individuals mentioned above. We are truly grateful for their contributions.
The Authors
Pashchimanchal Campus
Department of Electronics and Computer Engineering
Practical (45 hours)( ENSH 202 )
Programming language to be used: Python
Results to be visualized graphically wherever possible
Practical report contents: Working principle, Pseudocode, Source code, Test Cases
b = 2
print(b)
print(string1[1:3])
hi
Tuples
tup=("hello",1,2,3)
tup
('hello', 1, 2, 3)
List
l1=[1,2,"hello"]
l1
[1, 2, 'hello']
Dictionary
dict1={"a":1,2:"hi"}
dict1
{'a': 1, 2: 'hi'}
Set
s1=set({1,2,1,"hello"})
s1
{1, 2, 'hello'}
Control flow
IF else
a=10
b=20
if(a>b):
print("a is greater")
else:
print("b is greater")
b is greater
cmd="start"
match cmd:
case "start":
print("start the game")
case "stop":
print("stop the game")
For loop
for i in range(10):
print(i)
0
1
2
3
4
5
6
7
8
9
hello()
hello
import numpy as np
a= np.array([1,2,3,4])
array([1, 2, 3, 4])
import math
a=math.sqrt(18)
print(a)
4.242640687119285
Error handling
a=-2
try:
if a<0:
raise ValueError("Cannot find the squareroot of negative number")
b=math.sqrt(a)
print(b)
except ValueError as e:
print(e)
try:
c = 10 / 0 # This will raise a ZeroDivisionError
raise ValueError("Cannot divide by zero") # This won't execute
because of the above error
except ZeroDivisionError as e:
print("Caught a ZeroDivisionError:", e)
except ValueError as e:
print("Caught a ValueError:", e)
finally:
print("The error is of another source or handled completely.")
Caught a ZeroDivisionError: division by zero
The error is of another source or handled completely.
a=np.array([[1,2,3],[5,6,7]])
array([[1, 2, 3],
[5, 6, 7]])
b=np.array([[23,7,3],[8,6,9]])
array([[23, 7, 3],
[ 8, 6, 9]])
c=np.array([[4,5],[6,7],[9,0]])
array([[4, 5],
[6, 7],
[9, 0]])
d=np.dot(a,c)
e=a@c
array([[24, 9, 6],
[13, 12, 16]])
g=a*2
g
array([[ 2, 4, 6],
[10, 12, 14]])
h=a+c.T
array([[ 5, 8, 12],
[10, 13, 7]])
import numpy as np
a = np.array([1, 2, 3])
b = 5 # Scalar
result = a + b
print(result)
[6 7 8]
a = np.array([[1, 2, 3],
[4, 5, 6]])
b = np.array([10, 20, 30]) # Shape (3,)
result = a + b
print(result)
[[11 22 33]
[14 25 36]]
a = np.array([[1, 2, 3],
[4, 5, 6]])
b = np.array([[10],
[20]]) # Shape (2, 1)
result = a + b
print(result)
[[11 12 13]
[24 25 26]]
%matplotlib inline
import matplotlib.pyplot as plt
x = [0, 1, 2, 3]
y = [0, 1, 4, 9]
plt.plot(x, y)
plt.show()
Make a plot of the function f (x) = x2 for −5 ≤ x ≤ 5
%matplotlib inline
x = np.linspace(-5,5, 100)
plt.plot(x, x**2)
plt.show()
you can specify the color and format using the below table
%matplotlib inline
x = np.linspace(-5,5, 100)
plt.plot(x, x**2,"m-")
plt.show()
Make a plot of the function f (x) = x2 and g(x) = x3 for −5 ≤ x ≤ 5. Use different colors and markers
for each function.
x = np.linspace(-5,5,20)
plt.plot(x, x**2, "ko")
plt.plot(x, x**3, "r*")
plt.show()
plt.figure(figsize = (10,6))
x = np.linspace(-5,5,100)
plt.plot(x, x**2, "ko", label = "quadratic")
plt.plot(x, x**3, "r*", label = "cubic")
plt.title(f"Plot of Various Polynomials from {x[0]} to {x[-1]}")
plt.xlabel("X axis")
plt.ylabel("Y axis")
plt.legend(loc = 2)
plt.xlim(-6.6)
plt.ylim(-10,10)
plt.grid()
plt.show()
Given the lists x = np.arange(11) and y = x2, create a 2 × 3 subplot where each subplot plots x
versus y using plot, scatter, bar, loglog, semilogx, and semilogy. Title and label each plot
appropriately. Use a grid, but a legend is not necessary here.
x = np.arange(11)
y = x**2
plt.figure(figsize = (14, 8))
plt.subplot(2, 3, 1)
plt.plot(x,y)
plt.title("Plot")
plt.xlabel("X")
plt.ylabel("Y")
plt.grid()
plt.subplot(2, 3, 2)
plt.scatter(x,y)
plt.title("Scatter")
plt.xlabel("X")
plt.ylabel("Y")
plt.grid()
plt.subplot(2, 3, 3)
plt.bar(x,y)
plt.title("Bar")
plt.xlabel("X")
plt.ylabel("Y")
plt.grid()
plt.subplot(2, 3, 4)
plt.loglog(x,y)
plt.title("Loglog")
plt.xlabel("X")
plt.ylabel("Y")
plt.grid(which="both")
plt.subplot(2, 3, 5)
plt.semilogx(x,y)
plt.title("Semilogx")
plt.xlabel("X")
plt.ylabel("Y")
plt.grid(which="both")
plt.subplot(2, 3, 6)
plt.semilogy(x,y)
plt.title("Semilogy")
plt.xlabel("X")
plt.ylabel("Y")
plt.grid()
plt.tight_layout()
plt.show()
3D plotting
import numpy as np
from mpl_toolkits import mplot3d
import matplotlib.pyplot as plt
plt.style.use("seaborn-v0_8-poster")
fig = plt.figure(figsize = (10,10))
ax = plt.axes(projection="3d")
plt.show()
Consider the parameterized dataset: t is a vector from 0 to 10π with a step π/50, x = sin(t), and y
= cos(t). Make a 3D plot of the (x, y,t) dataset using plot3. Turn on the grid, make the axis equal,
and add axis labels and a title. Activate the i
%matplotlib inline
fig = plt.figure(figsize = (8,8))
ax = plt.axes(projection="3d")
ax.grid()
t = np.arange(0, 10*np.pi, np.pi/50)
x = np.sin(t)
y = np.cos(t)
ax.plot3D(x, y, t)
ax.set_title("3D Parametric Plot")
# Set axes label
ax.set_xlabel("x", labelpad=20)
ax.set_ylabel("y", labelpad=20)
ax.set_zlabel("t", labelpad=20)
plt.show()
<IPython.core.display.Javascript object>
<IPython.core.display.HTML object>
<IPython.core.display.Javascript object>
<IPython.core.display.HTML object>
Make a 3D scatter plot with randomly generated 50 data points for x, y, and z. Set the point color
as red and size of the point as 50.
%matplotlib inline
x = np.random.random(50)
y = np.random.random(50)
z = np.random.random(50)
fig = plt.figure(figsize = (10,10))
ax = plt.axes(projection="3d")
ax.grid()
ax.scatter(x, y, z, c = "r", s = 50)
ax.set_title("3D Scatter Plot")
# Set axes label
ax.set_xlabel("x", labelpad=20)
ax.set_ylabel("y", labelpad=20)
ax.set_zlabel("z", labelpad=20)
plt.show()
Make a plot of the surface f (x, y) = sin(x) · cos(y) for −5 ≤ x ≤ 5,−5 ≤ y ≤ 5 using the plot_surface
function. Take care to use a sufficiently fine discretization in x and y so that the plot looks
smooth.
fig = plt.figure(figsize = (12,10))
ax = plt.axes(projection="3d")
x = np.arange(-5, 5.1, 0.2)
y = np.arange(-5, 5.1, 0.2)
X, Y = np.meshgrid(x, y)
Z = np.sin(X)*np.cos(Y)
surf = ax.plot_surface(X, Y, Z, cmap = plt.cm.cividis)
# Set axes label
ax.set_xlabel("x", labelpad=20)
ax.set_ylabel("y", labelpad=20)
ax.set_zlabel("z", labelpad=20)
fig.colorbar(surf, shrink=0.5, aspect=8)
plt.show()
Make a 1 × 2 subplot to plot the above X, Y, Z data in a wireframe plot and a surface plot
fig = plt.figure(figsize=(12,6))
ax = fig.add_subplot(1, 2, 1, projection="3d")
ax.plot_wireframe(X,Y,Z)
ax.set_title("Wireframe plot")
ax = fig.add_subplot(1, 2, 2, projection="3d")
ax.plot_surface(X,Y,Z)
ax.set_title("Surface plot")
plt.tight_layout()
plt.show()
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as manimation
n = 1000
x = np.linspace(0, 6*np.pi, n)
y = np.sin(x)
# Define the meta data for the movie
FFMpegWriter = manimation.writers["ffmpeg"]
metadata = dict(title="Movie Test", artist="Matplotlib",
comment="a red circle following a blue sine wave")
writer = FFMpegWriter(fps=15, metadata=metadata)
# Initialize the movie
fig = plt.figure()
# plot the sine wave line
sine_line, = plt.plot(x, y, "b")
red_circle, = plt.plot([], [], "ro", markersize = 10)
plt.xlabel("x")
plt.ylabel("sin(x)")
# Update the frames for the movie
with writer.saving(fig, "writer_test.mp4", 100):
for i in range(n):
x0 = x[i]
y0 = y[i]
red_circle.set_data(x0, y0)
writer.grab_frame()
<ipython-input-91-1c65db709537>:24: MatplotlibDeprecationWarning:
Setting data with a non sequence type is deprecated since 3.7 and will
be remove two minor releases later
red_circle.set_data(x0, y0)
LAB 2
1. Bisection method
Working principle The Bisection Method is a root-finding algorithm that repeatedly bisects an
interval and then selects the subinterval in which a root must lie. The method requires two initial
points, a and b, such that the function values at these points have opposite signs. The algorithm
iteratively narrows down the search interval by calculating the midpoint and evaluating the
function at this point until the root is approximated to a desired precision.
Pseudo code:
1. Given initial values a, b, and tolerance ε.
a. Find midpoint m = (a + b) / 2.
Source code:
import numpy as np
import matplotlib.pyplot as plt
from cmath import sin
%matplotlib inline
Arguments:
f -- Function whose root we are trying to find
a -- Left boundary of the interval
b -- Right boundary of the interval
tol -- Tolerance value for stopping the iteration
Returns:
m -- Approximate root
"""
# Check if a and b bound a root
if np.sign(f(a)) == np.sign(f(b)):
raise Exception("The scalars a and b do not bound a root")
# Get midpoint
m = (a + b) / 2
def f(x):
return x**3 - 4
# Bisection method
root = my_bisection(f, 1, 3, 0.01)
print("Root:", root)
Root: 1.587890625
def func(x):
return x**3 - 5 * x - 9
iteration += 1
def main():
print("BISECTION METHOD IMPLEMENTATION")
print()
# Default values
function_str = "x**3 - 5 * x - 9"
a, b, tolerance = 2, 3, 0.0001
print(f"f(x) = {function_str}")
print(f"a = {a}, b = {b}, tolerance = {tolerance}")
print()
if __name__ == "__main__":
main()
Output:
BISECTION METHOD IMPLEMENTATION
f(x) = x**3 - 5 * x - 9
a = 2, b = 3, tolerance = 0.0001
Test cases
Test for f(x) = x^2 - 4 in the interval [1, 3] with tolerance 0.01.
Test for f(x) = cos(x) - x in the interval [0, 1] with tolerance 0.001
2.Secant method
Working Principle: The Secant Method is an iterative technique for finding the root of a function.
It uses two initial guesses and approximates the root by the intersection of secant lines drawn
through successive points on the graph of the function. The method avoids the need for the
derivative of the function but still converges to the root under the right conditions.
Pseudocode:
Code:
def f(x):
return 3*x**2 - 4*x+7
# Secant method
root = secant_method(f, 1, 2, 0.01)
def example_function(x):
# Example function: f(x) = x^3 - 5x - 9
return x**3 - 5 * x - 9
def get_custom_function():
# Prompt user to input a custom function as a string
function_expression = input("Provide a mathematical function using 'x'
(e.g., x**3 - 5*x - 9): ")
x = sp.symbols('x')
symbolic_function = sp.sympify(function_expression)
numeric_function = sp.lambdify(x, symbolic_function, modules=['numpy'])
return numeric_function, symbolic_function
def main():
print("IMPLEMENTATION OF THE SECANT METHOD")
print()
if __name__ == "__main__":
main()
Output:
IMPLEMENTATION OF THE SECANT METHOD
Test Cases:
Test for f(x) = x^2 - 4 starting with initial guesses x0 = 1 and x1 = 2 with tolerance 0.01.
Test for f(x) = e^x - 2x starting with initial guesses x0 = 0 and x1 = 1 with tolerance 0.001.
1. Given initial guess x0, tolerance ε, and the derivative of the function f'(x).
b. Set x0 = x1.
3. Return the root approximation x1.
Code:
def newton_raphson(f, df, x0, tol=1e-6, max_iter=100):
x = x0
for i in range(max_iter):
x_new = x - f(x) / df(x)
print(f"Iteration {i+1}: x = {x_new:.6f}, f(x) = {f(x_new):.6f}")
if abs(f(x_new)) < tol:
return x_new
x = x_new
return None
f = lambda x: x**3 - 5 * x - 9
df = lambda x: 3 * x**2 - 5
root = newton_raphson(f, df, x0=2.0)
print(f"Root: {root:.6f}")
Iteration 1: x = 3.571429, f(x) = 18.696793
Iteration 2: x = 3.009378, f(x) = 3.207103
Iteration 3: x = 2.864712, f(x) = 0.185915
Iteration 4: x = 2.855236, f(x) = 0.000771
Iteration 5: x = 2.855197, f(x) = 0.000000
Root: 2.855197
def newton_raphson(f, df, x0, tol):
while abs(f(x0)) > tol:
x0 = x0 - f(x0) / df(x0)
return x0
def df(x):
return 2*x
# Newton-Raphson method
root = newton_raphson(f, df, 2, 0.01)
def f(x):
return x**3 - 5 * x - 9
def g(x):
return 3 * x**2 - 5
def func_input():
function_str = input("Enter your function (use 'x' as the variable)
(Example: x**3 - 5 * x - 9): ")
x = sp.symbols('x')
sp_function = sp.sympify(function_str)
func = sp.lambdify(x, sp_function, modules=['numpy'])
return func, sp_function
def func_derivative(func):
x = sp.symbols('x')
sp_derivative = sp.diff(func, x)
derivative = sp.lambdify(x, sp_derivative, modules=['numpy'])
return derivative, sp_derivative
# Update guess
next_guess = current_guess - func(current_guess) /
derivative(current_guess)
print(f"Iteration-{step}, x = {next_guess:.6f}, f(x) =
{f(next_guess):.6f}")
print()
current_guess = next_guess
def main():
print("NEWTON-RAPHSON METHOD IMPLEMENTATION")
print()
print(f"f(x) = {function_str}")
print(f"g(x) = {derivative_str}")
print(f"Initial guess={initial_guess} Max steps={max_steps}
tolerance={tolerance}")
print()
# Perform the Newton-Raphson method
newton_raphson_method(func, derivative, initial_guess, tolerance,
max_steps)
if __name__ == "__main__":
main()
Test Cases:
Test for f(x) = x^2 - 4 and its derivative f'(x) = 2x, starting with initial guess x0 = 2 and
tolerance 0.01.
Test for f(x) = e^x - 2x and its derivative f'(x) = e^x - 2, starting with initial guess x0 = 1 and
tolerance 0.001.
Pseudo code:
1. Given initial guesses x0 for the system, tolerance ε, and the Jacobian matrix J(x).
a. Repeat until the difference between successive approximations is less than ε:
Compute the function vector F(x) and Jacobian matrix J(x).
import numpy as np
import matplotlib.pyplot as plt
def f2(x):
return x[0]**3 + x[1]**3 - 1
# Function vector
def f(x):
return np.array([f1(x), f2(x)])
# Initial guess
x0 = np.array([1.0, 1.0])
plt.xlabel('x')
plt.ylabel('y')
plt.title('System of Non-Linear Equations - Newton-Raphson
Method')
plt.legend()
plt.grid(True)
plt.show()
else:
print("No solution found.")
The Gauss-Jordan method is an algorithm for solving a system of linear equations. It transforms the
augmented matrix into a reduced row echelon form (RREF) using row operations. The solution is
obtained directly from the final matrix.
Pseudo code Input: Augmented matrix [A|b] representing the system of linear equations Ax = b Output:
Solution vector x
- Find the pivot element A[i, i] (diagonal element of the current row).
- If the pivot is not 1, divide the entire row i by the pivot (A[i, i]). - This will make
A[i, i] = 1.
b. For each other row j (j ≠ i), eliminate the variable corresponding to the current
column (A[i, i]):
4. After all rows have been processed, the augmented matrix will be in reduced row
echelon form (RREF):
– The left n x n part of the augmented matrix [A|b] will be an identity matrix I.
– The right column will contain the solution vector x.
5. Return the solution vector x (the last column of the augmented matrix).
x =LastColumn(augmentedMatrix)
import numpy as np
def gauss_jordan(A, b):
n = len(b)
AugmentedMatrix = np.hstack([A, b.reshape(-1, 1)]) # Create
augmented matrix [A|b]
# Example Usage
A = np.array([[2, -1, 1], [1, 3, 2], [1, 1, 1]], dtype=float)
b = np.array([2, 12, 5], dtype=float)
solution = gauss_jordan(A, b)
print("Solution:", solution)
ax.set_xlabel('X axis')
ax.set_ylabel('Y axis')
ax.set_zlabel('Z axis')
plt.show()
import numpy as np
import sys
def read_augmented_matrix(n):
print("Enter Augmented Matrix Coefficients:")
matrix = np.zeros((n, n + 1))
for i in range(n):
for j in range(n + 1):
matrix[i][j] = float(input(f"a[{i}][{j}] = "))
print(matrix)
return matrix
def gauss_jordan_elimination(a):
n = len(a)
for i in range(n):
# Check for divide-by-zero
if a[i][i] == 0.0:
sys.exit("Divide by zero detected in pivot element!")
for j in range(n):
if i != j:
ratio = a[j][i] / a[i][i]
for k in range(n + 1):
a[j][k] -= ratio * a[i][k]
# Extracting the solution
x = np.zeros(n)
for i in range(n):
x[i] = a[i][n] / a[i][i]
return x
def main():
print("GAUSS-JORDAN ELIMINATION METHOD")
print()
if default:
# Default 3x4 augmented matrix
n = 3
a = np.array([
[2, 1, -1, 8],
[-3, -1, 2, -11],
[-2, 1, 2, -3]
], dtype=float)
print("Using Default Matrix:")
print(a)
else:
n = int(input("Enter number of unknowns: "))
a = read_augmented_matrix(n)
if __name__ == "__main__":
main()
Forward Elimination: Using row operations to eliminate variables in each column and create an
upper triangular matrix. For each column, identify the largest absolute value in that column (pivot)
and swap rows. Eliminate all entries below the pivot using row operations. Back Substitution: After
obtaining the upper triangular matrix, solve for the unknowns by substituting values from the last
row upwards.
Pseudocode:
Input: Augmented matrix [A|b] of size n x n+1 (for system Ax = b) Output: Solution vector x of size
n
1. For i = 1 to n-1:
a. Find the pivot row with the largest absolute value in column i.
b. Swap the current row with the pivot row.
c. For j = i+1 to n: - Eliminate the i-th variable from row j: Row[j] = Row[j] - (A[j, i] / A[i, i])
* Row[i]
2. Back Substitution:
a. For i = n-1 to 0: - x[i] = (b[i] - sum(A[i, j] * x[j] for j = i+1 to n)) / A[i, i]
3. Return the solution vector x.
import numpy as np
# Forward Elimination
for i in range(n):
# Pivoting: Find the maximum element in the current column
max_row = np.argmax(np.abs(AugmentedMatrix[i:n, i])) + i
AugmentedMatrix[[i, max_row]] = AugmentedMatrix[[max_row, i]]
# Swap rows
# Back Substitution
x = np.zeros(n)
for i in range(n-1, -1, -1):
x[i] = (AugmentedMatrix[i, -1] - np.dot(AugmentedMatrix[i,
i+1:n], x[i+1:n])) / AugmentedMatrix[i, i]
return x
# Example Usage
A = np.array([[3, -2, 5], [1, 1, -3], [2, 3, 1]], dtype=float)
b = np.array([3, 3, 4], dtype=float)
solution = gauss_elimination_partial_pivoting(A, b)
print("Solution:", solution)
ax.set_xlabel('X axis')
ax.set_ylabel('Y axis')
ax.set_zlabel('Z axis')
plt.show()
2y + 5z = 3 x + y - 3z = 3 2x + 3y + z = 4
3.Gauss-Seidal method
Working Principle: The Gauss-Seidel method is an iterative technique that modifies an initial guess
for the solution vector x=(x 1 ,x 2 ,...,x n ) to approach the true solution of the system of equations 𝐴
𝑥 = 𝑏. The method updates each variable in turn using the most recent values available for the other
variables.
Steps:
where 𝑎𝑖𝑗 are the coefficients of the matrix 𝑨, and 𝑏𝑖 are the elements of the vector 𝒃.
3. Repeat the process until the solution converges, i.e., the difference between the new and old solutions is
Pseudocode:
Input: Coefficient matrix A (n x n), vector b (n x 1), initial guess x0 (n x 1), tolerance tol, max
iterations max_iter Output: Solution vector x (n x 1)
# Example Usage
A = np.array([[4, -1, 0, 0],
[-1, 4, -1, 0],
[0, -1, 4, -1],
[0, 0, -1, 3]], dtype=float)
x0 = np.zeros_like(b)
for k in range(max_iter):
x_old = np.copy(x)
return x
4x - y = 15 -x + 4y - z = 10 -y + 4z - w = 10 -z + 3w = 10
4.Power Method
Working Principle:
The Power Method is an iterative technique used to find the largest eigenvalue and its corresponding
eigenvector of a matrix. The method works as follows:
𝑦𝑘 = 𝐴 ⋅ 𝑥𝑘
𝑦𝑘
𝑥𝑘+1 =
|𝑦𝑘 |
𝑥𝑘𝑇 ⋅ 𝐴 ⋅ 𝑥𝑘
𝜆𝑘 =
𝑥𝑘𝑇 ⋅ 𝑥𝑘
5. Repeat the process until the difference between consecutive eigenvalue estimates is smaller than a
given tolerance.
Pseudocode: Input: Matrix A, Initial vector x0, Tolerance tol, Maximum iterations max_iter
Output: Eigenvalue λ, Eigenvector x
2. Normalize x0
3. Set λ_old to 0
4. For k = 1 to max_iter:
c. Compute the Rayleigh quotient to estimate λ_k: λ_k = (x_k^T * A * x_k) / (x_k^T *
x_k)
lambda_old = lambda_new
return lambda_new, x
def read_matrix(n):
matrix = np.zeros((n, n))
print("Enter Matrix Coefficients:")
for i in range(n):
for j in range(n):
matrix[i][j] = float(input(f"a[{i}][{j}] = "))
return matrix
def read_vector(n):
vector = np.zeros(n)
print("Enter Initial Guess Vector:")
for i in range(n):
vector[i] = float(input(f"x[{i}] = "))
return vector
# Display results
print()
print(f"STEP {step}")
print("-" * 10)
print(f"Eigenvalue = {lambda_new:.4f}")
print("Eigenvector:")
print("[", end=" ")
for val in x:
print(f"{val:.4f}", end="\t")
print("]")
# Check convergence
error = abs(lambda_new - lambda_old)
print(f"Error = {error:.6f}")
if error < tolerable_error:
print()
print("Converged!")
break
lambda_old = lambda_new
step += 1
def main():
print("POWER METHOD IMPLEMENTATION")
print()
if default:
# Default matrix and vector
a = np.array([[2, 1, 1],
[1, 3, 2],
[1, 0, 0]], dtype=float)
x = np.array([1, 1, 1], dtype=float)
tolerable_error = 0.0001
max_iterations = 100
print("Using Default Values:")
print("Matrix:")
print(a)
print("Initial Guess Vector:")
print(x)
print()
print(f"Tolerable Error: {tolerable_error}")
print(f"Maximum Iterations: {max_iterations}")
else:
# User input
n = int(input("Enter order of matrix: "))
a = read_matrix(n)
x = read_vector(n)
tolerable_error = float(input("Enter tolerable error: "))
max_iterations = int(input("Enter maximum number of steps: "))
if __name__ == "__main__":
main()
STEP 1
----------
Eigenvalue = 6.0000
Eigenvector:
[ 0.6667 1.0000 0.1667 ]
Error = 5.000000
STEP 2
----------
Eigenvalue = 4.0000
Eigenvector:
[ 0.6250 1.0000 0.1667 ]
Error = 2.000000
STEP 3
----------
Eigenvalue = 3.9583
Eigenvector:
[ 0.6105 1.0000 0.1579 ]
Error = 0.041667
STEP 4
----------
Eigenvalue = 3.9263
Eigenvector:
[ 0.6059 1.0000 0.1555 ]
Error = 0.032018
STEP 5
----------
Eigenvalue = 3.9169
Eigenvector:
[ 0.6044 1.0000 0.1547 ]
Error = 0.009426
STEP 6
----------
Eigenvalue = 3.9138
Eigenvector:
[ 0.6039 1.0000 0.1544 ]
Error = 0.003132
STEP 7
----------
Eigenvalue = 3.9127
Eigenvector:
[ 0.6037 1.0000 0.1543 ]
Error = 0.001026
STEP 8
----------
Eigenvalue = 3.9124
Eigenvector:
[ 0.6037 1.0000 0.1543 ]
Error = 0.000337
STEP 9
----------
Eigenvalue = 3.9123
Eigenvector:
[ 0.6036 1.0000 0.1543 ]
Error = 0.000111
STEP 10
----------
Eigenvalue = 3.9122
Eigenvector:
[ 0.6036 1.0000 0.1543 ]
Error = 0.000036
Test Cases:
4 1
Input: A= ,
2 3
Initial guess =[0.5,0.5]
The forward differences are calculated using the following recursive relations:
import numpy as np
import matplotlib.pyplot as plt
return diff_table
term /= np.math.factorial(i)
result += term
return result
# Value to interpolate
x_value = 3
Test Case 2:
Test Case 3:
2.Lagrange interpolation
Working Principle:
Lagrange Interpolation is a method for estimating the values of a function given a set of data points. It
involves constructing a polynomial that passes through all the given points. The general form of the
Lagrange polynomial is:
{𝒏}
𝑳𝒊(𝒙) = ∏ {𝒙 − 𝒙𝒋 }/{𝒙𝒊 − 𝒙𝒋 }
{𝒋=𝟎,𝒋≠𝒊}
import numpy as np
import matplotlib.pyplot as plt
return result
# Value to interpolate
x_value = 2.5
def read_data_points(n):
x = np.zeros(n)
y = np.zeros(n)
print("Enter data for x and y: ")
for i in range(n):
x[i] = float(input(f"x[{i}] = "))
y[i] = float(input(f"y[{i}] = "))
return x, y
def main():
print("LAGRANGE INTERPOLATION")
print()
if default:
x = np.array([0, 1, 2, 3], dtype=float)
y = np.array([1, 2, 0, 5], dtype=float)
print("Using Default Data Points:")
print(f"x: {x}")
print(f"y: {y}")
else:
n = int(input("Enter number of data points: "))
x, y = read_data_points(n)
# Displaying output
print(f"Interpolated value at {xp:.3f} is {yp:.3f}.")
if __name__ == "__main__":
main()
LAGRANGE INTERPOLATION
Test cases
Test Case 1:
Test Case 2:
𝑀𝑖𝑛𝑖𝑚𝑖𝑧𝑒 ∑ 𝑦𝑖 − (𝑚 𝑥𝑖 + 𝑐)2
{𝑖=1}
Exponential Regression:
For exponential data, the model is
𝑦 = 𝑎 𝑒 𝑏𝑥
By taking the logarithm of both sides, the exponential model is linearized to:
ln(y)=ln(a)+bx
Pseudocode: Input: Data points (x_values, y_values) Output: Coefficients m (slope) and c (intercept)
2. Compute the terms needed for the slope (m) and intercept (c):
Input: Data points (x_values, y_values), degree of polynomial (n) Output: Coefficients a_n, a_(n- 1), ...,
a_0
# Linear regression
m, c = linear_regression(x_values, y_values_linear)
print(f"Linear Regression: y = {m}x + {c}")
# Exponential regression
a, b = exponential_regression(x_values, y_values_exponential)
print(f"Exponential Regression: y = {a}e^{b}x")
plt.tight_layout()
plt.show()
def read_data(n):
x = np.zeros(n)
y = np.zeros(n)
print("Enter data points:")
for i in range(n):
x[i] = float(input(f"x[{i}] = "))
y[i] = float(input(f"y[{i}] = "))
return x, y
def main():
print("LEAST SQUARES METHOD FOR LINEAR REGRESSION")
print()
if default:
x = np.array([1, 2, 3, 4, 5], dtype=float)
y = np.array([2.2, 2.8, 4.5, 3.7, 5.5], dtype=float)
print("Using Default Data Points:")
print("x:", x)
print("y:", y)
else:
# User input
n = int(input("How many data points? "))
x, y = read_data(n)
# Calculate coefficients
a, b = calculate_coefficients(x, y)
# Display results
display_results(a, b)
if __name__ == "__main__":
main()
Coefficients are:
a (Intercept): 1.4900
b (Slope): 0.7500
And the equation is: y = 1.4900 + 0.7500x
Test Cases:
Test Case 1:
• Input: x_values = [1, 2, 3, 4, 5], y_values_exponential = [2.7, 7.4, 20.1, 54.6, 148.4]
• Expected Output: Exponential regression should return a fit like y=2.7𝑒 0.7𝑥 .
Test Case 3:
𝑆𝑖(𝑥) = 𝑎𝑖 (𝑥 − 𝑥𝑖 )3 + 𝑏𝑖 (𝑥 − 𝑥𝑖 )2 + 𝑐𝑖 (𝑥 − 𝑥𝑖 ) + 𝑑𝑖
Where (ai , bi , ci , di) are the coefficients for the spline in the interval.
2. Solve for the coefficients: The system of equations is set up by applying the following conditions:
o The spline must pass through all the data points: 𝑆𝑖(𝑥𝑖 ) = 𝑦𝑖 .
o The first and second derivatives must be continuous at the data points.
o Boundary conditions: Typically, natural splines are used where the second derivative at the
endpoints is zero.
3. Solve the linear system: A tridiagonal system of linear equations is formed, and solving this system
yields the spline coefficients.
Pseudocode:
Input: Data points (x_values, y_values)
for i in range(n-1):
b[i] = (y[i+1] - y[i]) / h[i] - h[i] * (2 * c[i] + c[i+1]) / 3
d[i] = (c[i+1] - c[i]) / (3 * h[i])
return a, b, c[:-1], d
• Input:
o x_values = [1, 2, 3, 4, 5]
o y_values = [2, 3, 5, 7, 11]
• Expected Output: A smooth cubic spline curve passing through all the points.
Test Case 2:
• Input:
o x_values = [1, 3, 5, 7, 9]
o y_values = [1, 4, 9, 16, 25]
• Expected Output: A cubic spline curve fitting the parabolic shape.
Test Case 3:
• Input:
o x_values = [0, 1, 2, 3]
o y_values = [1, 2, 0, 3]
• Expected Output: A cubic spline curve passing through these data points with a smooth transition.
LAB 5
1.Trapezoidal rule
Working Principle:
The Trapezoidal Rule is a numerical method for approximating the definite integral of a function. It works
by dividing the area under the curve into trapezoidal segments rather than rectangles (as in the Riemann
sum method). The area of each trapezoid is calculated and summed to provide an estimate for the total area
under the curve.
𝑛−1
ℎ
𝐼 ≈ ( 𝑓(𝑎) + 2 ∑ 𝑓(𝑥𝑖 ) + 𝑓(𝑏))
2
𝑖=1
Where:
𝑏−𝑎
• ℎ = 𝑛 𝑖𝑠 𝑡ℎ𝑒 𝑤𝑖𝑑𝑡ℎ 𝑜𝑓 𝑒𝑎𝑐ℎ 𝑠𝑢𝑏𝑖𝑛𝑡𝑒𝑟𝑣𝑎𝑙.
• 𝑥𝑖 = 𝑎 + 𝑖 ⋅ ℎ 𝑓𝑜𝑟 𝑖 = 1, 2, … , 𝑛 𝑎𝑟𝑒 𝑡ℎ𝑒 𝑖𝑛𝑡𝑒𝑟𝑚𝑒𝑑𝑖𝑎𝑡𝑒 𝑝𝑜𝑖𝑛𝑡𝑠.
• f(a) and f(b) are the function values at the endpoints of the interval.
Pseudocode: Input: Function f(x), interval [a, b], number of subintervals n Output: Approximate integral
I
1. Calculate the width of each subinterval: h = (b - a) / n
2. Initialize sum to 0: sum = 0
3. Loop through the intermediate points: for i = 1 to n-1: x = a + i * h sum = sum + f(x)
4. Add the function values at the endpoints to the sum: sum = sum + (f(a) + f(b)) / 2
5. Multiply the sum by the width of each subinterval: I = h * sum
6. Return the approximate integral I.
import numpy as np
import matplotlib.pyplot as plt
# Initialize sum
sum = 0.5 * (f(a) + f(b))
def f(x):
return 1 / (1 + x**2)
def func_input():
function_str = input("Enter your function (use 'x' as the variable)
(Example: 1/x): ")
x = sp.symbols('x')
sp_function = sp.sympify(function_str)
func = sp.lambdify(x, sp_function, modules=['numpy'])
return func, sp_function
def main():
print("Trapezoidal Rule for Numerical Integration")
print()
print(f"f(x) = {function_str}")
print(f"Lower Limit: {lower_limit}")
print(f"Upper Limit: {upper_limit}")
print(f"Subintervals: {sub_interval}")
print()
print("Integration Result:")
print(f"Using the Trapezoidal Method, the approximate value is:
{result:.6f}")
if __name__ == "__main__":
main()
Integration Result:
Using the Trapezoidal Method, the approximate value is: 0.784241
Test Case:
Test Case 1:
• Function: f(x)=𝑥 2
• Interval: [0,2][0, 2]
• Number of subintervals: n=10
• Expected Output: The approximate integral using the trapezoidal rule should be close to the exact
integral 8/3 = 2.666738.
Test Case 2:
• Function: f(x)=𝑒 𝑥
• Interval: [0,1]
• Number of subintervals: n=20
• Expected Output: The approximate integral should be close to the exact value of 𝑒 𝑥 from 0 to 1,
which is approximately 1.71828.
2.Simpson’s 1/3 rule or Simpson’s 3/8 rule
Working Principle
Simpson’s 1/3 Rule:
Simpson's 1/3 Rule is a method of numerical integration that approximates the integral of a function by
dividing the area under the curve into a series of parabolic segments. It uses quadratic polynomials to
estimate the area.
ℎ
𝐼 ≈ ( 𝑓(𝑎) + 4 ∑ 𝑓(𝑥𝑖 ) + 2 ∑ 𝑓(𝑥𝑖 ) + 𝑓(𝑏))
3
{𝑖=1,3,5,… } {𝑖=2,4,6,… }
Where:
𝑏−𝑎
• h= is the width of each subinterval.
𝑛
• 𝑥𝑖 are the intermediate points, with odd indices receiving a weight of 4 and even indices receiving a
weight of 2.
Simpson's 3/8 Rule is another method for approximating the definite integral of a function, and it is a bit
more accurate than Simpson's 1/3 Rule for certain functions. It approximates the integral by fitting cubic
polynomials to the data.
3ℎ
𝐼 ≈ ( 𝑓(𝑎) + 3 ∑ + 3 ∑ + 𝑓(𝑏))
8
{𝑖=1,4,7,… } {𝑖=2,5,8,… }
Where:
𝑏−𝑎
• h= 𝑛
is the width of each subinterval.
Input: Function f(x), interval [a, b], number of subintervals n (n must be even) Output: Approximate
integral I
I = (h / 3) * sum
6. Return the approximate integral I
Pseudocode:simpson's 3/8 rule
Input: Function f(x), interval [a, b], number of subintervals n (n must be a multiple of 3) Output:
Approximate integral I
3. Loop through the points and add weighted contributions: for i = 1, 4, 7, ..., n-2: sum
+= 3 * f(a + i * h)
4. Loop through the points and add weighted contributions: for i = 2, 5, 8, ..., n-1: sum
+= 3 * f(a + i * h)
I = (3 * h / 8) * sum
6. Return the approximate integral I
import numpy as np
import matplotlib.pyplot as plt
# Visualization
x_values = np.linspace(a, b, 100)
y_values = example_function(x_values)
plt.figure(figsize=(8, 6))
plt.plot(x_values, y_values, label='f(x) = x^2', color='blue')
import sympy as sp
def f(x):
return 1 / (1 + x**2)
def func_input():
function_str = input("Enter your function (use 'x' as the variable)
(Example: 1/x): ")
x = sp.symbols('x')
sp_function = sp.sympify(function_str)
func = sp.lambdify(x, sp_function, modules=['numpy'])
return func, sp_function
def simpson13(func, x0, xn, n):
if n % 2 != 0:
raise ValueError("Number of subintervals (n) must be even.")
def main():
print("Simpson's 1/3 Rule for Numerical Integration")
print()
print(f"f(x) = {function_str}")
print(f"Lower Limit: {lower_limit}")
print(f"Upper Limit: {upper_limit}")
print(f"Subintervals: {sub_interval}")
print()
if __name__ == "__main__":
main()
Integration Result:
Using Simpson's 1/3 method, the approximate value is: 0.785398
3.Boole’s Rule or Weddle’s Rule
Working Principle
Boole’s Rule (Weddle’s Rule) is a higher-order numerical integration method that approximates the
integral of a function using a polynomial of degree four (quartic polynomial). It is a specific case of a more
general family of Newton-Cotes formulas.
• Formula: The formula for Boole's Rule (also known as Weddle's Rule) for approximating the
integral of a function f(x)f(x)f(x) over the interval [a,b] is given by:
𝑏 2(𝑏−𝑎) 𝑎+𝑏 𝑎+3𝑏 3𝑎+𝑏
• ∫𝑎 𝑓(𝑥) 𝑑𝑥 ≈ 45 [ 7𝑓(𝑎) + 32𝑓 ( 2 ) + 12𝑓 ( 4 ) + 32𝑓 ( 4 ) + 7𝑓(𝑏)]
• Description:
o Step 1: Divide the integral into subintervals.
o Step 2: Use weighted averages of function values at specific points in the interval to
approximate the area under the curve.
o Step 3: The result gives a good approximation with high accuracy for polynomial functions
and smooth curves.
Input: Function f(x), Lower limit a, Upper limit b Output: Approximate integral value
# Shading the area under the curve for integration using Boole's Rule
x_boole = np.array([a, a + (b - a) / 4, a + 2 * (b - a) / 4, a + 3 *
(b - a) / 4, b])
y_boole = example_function(x_boole)
plt.fill_between(x_boole, 0, y_boole, color='orange', alpha=0.3,
label="Boole's Rule Approximation")
Test Case 1:
• Function: f(x)=𝑥 2
• Interval: [0, 2]
23 −03
• Exact Integral: 3 ≈2.6667
• Expected Output: Approximate integral ≈ 2.6667
Test Case 2:
• Function: f(x)= 𝑒 𝑥
• Interval: [0, 1]
• Exact Integral: 𝑒 1 −𝑒 0 =e−1≈1.7183
• Expected Output: Approximate integral ≈ 1.7183
Gauss-Legendre integration
Working Principle
Gauss-Legendre Integration is a numerical method for approximating definite integrals using orthogonal
polynomials. It is based on the concept that the integral:
𝑏
∫ 𝑓(𝑥) 𝑑𝑥
𝑎
can be approximated as:
𝑏 𝑛
∫ 𝑓(𝑥) 𝑑𝑥 ≈ ∑ 𝑤𝑖 ⋅ 𝑓(𝑥𝑖 )
𝑎 𝑖=1
Here:
The method transforms the interval [a,b] to [−1,1]for computation and uses tabulated weights and nodes.
Pseudocode
1. Input:
o Function f(x) lower limit a, upper limit b, and number of nodes n.
2. Steps:
1. Retrieve the roots (𝑥𝑖 ) and weights (𝑤𝑖 ) for the Legendre polynomial of degree nnn.
2. Map the roots from the interval [−1,1to [a,b]:
𝑏−𝑎 𝑏+𝑎
𝑥𝑚 = ⋅ 𝑥𝑖 +
2 2
𝑏−𝑎
𝑤𝑚 = ⋅ 𝑤𝑚
2
Integral=∑𝑛{𝑖=1} ⋅ 𝑓(𝑥𝑚 )
3. Output:
o Approximate integral value.
import numpy as np
import matplotlib.pyplot as plt
from scipy.special import roots_legendre
# Function to integrate
def function_to_integrate(x):
return x**2 # Example: f(x) = x^2
# Visualization
x = np.linspace(a, b, 100)
y = function_to_integrate(x)
𝑘1 = ℎ ⋅ 𝑓(𝑥𝑛 , 𝑦𝑛 )
ℎ 𝑘1
𝑘2 = ℎ ⋅ 𝑓 (𝑥𝑛 + , 𝑦𝑛 + )
2 2
ℎ 𝑘2
𝑘3 = ℎ ⋅ 𝑓 (𝑥𝑛 + , 𝑦𝑛 + )
2 2
𝑘4 = ℎ ⋅ 𝑓(𝑥𝑛 + ℎ, 𝑦𝑛 + 𝑘3 )
1
𝑦{𝑛+1} = 𝑦𝑛 + (𝑘1 + 2𝑘2 + 2𝑘3 + 𝑘4 )
6
Here:
• h: Step size.
• 𝑘1 , 𝑘2 , 𝑘3 , 𝑘4 ): Intermediate slopes computed at different points within the interval.
Pseudo code:
1. Input:
o Function f(x,y), initial condition 𝑥0 , 𝑦0 , step size h, and the interval [𝑥0 , 𝑥𝑒𝑛𝑑 .
2. Steps:
1. Set x=𝑥0 ,y=𝑥0 .
2. Repeat until x≤𝑥𝑒𝑛𝑑
▪ Compute 𝑘1 , 𝑘2 , 𝑘3 , 𝑘4 using the equations above.
1
▪ Update y=y+ (𝑘1 + 2𝑘2 + 2𝑘3 + 𝑘4 ).
6
▪ Increment x=x+h.
3. Store and return x,y
3. Output:
o Solution y(x) over the interval.
import numpy as np
import matplotlib.pyplot as plt
x = x0
y = y0
x_values.append(x)
y_values.append(y)
return x_values, y_values
# Inputs
x0 = 0 # Initial x
y0 = 1 # Initial y
h = 0.1 # Step size
x_end = 2 # End point of x
# Print results
print("x values:", x_values)
print("RK4 y values:", y_values)
print("Exact y values:", exact_y_values)
# Visualization
plt.figure(figsize=(8, 6))
plt.plot(x_values, y_values, 'o-', label="RK4 Approximation",
color='blue')
plt.plot(x_values, exact_y_values, 'r-', label="Exact Solution",
color='red')
plt.title("Solution of ODE using RK4 Method")
plt.xlabel("x")
plt.ylabel("y")
plt.legend()
plt.grid()
plt.show()
import sympy as sp
def func_input():
function_str = input("Enter your function (use 'x' and 'y' as the
variables) (Example: x + y): ")
x, y = sp.symbols('x y')
sp_function = sp.sympify(function_str)
func = sp.lambdify((x, y), sp_function, modules=['numpy'])
return func, sp_function
print('---------'*4)
print('x0\ty0\tyn')
print('---------'*4)
for i in range(n):
k1 = h * func(x0, y0)
k2 = h * func(x0 + h/2, y0 + k1/2)
k3 = h * func(x0 + h/2, y0 + k2/2)
k4 = h * func(x0 + h, y0 + k3)
k = (k1 + 2 * k2 + 2 * k3 + k4) / 6
yn = y0 + k
print(f'{x0:.4f}\t{y0:.4f}\t{yn:.4f}')
print('---------'*4)
y0 = yn
x0 = x0 + h
print()
print(f'At x={xn:.4f}, y={yn:.4f}')
def main():
print("Runge-Kutta 4th Order (RK4) Method for Solving ODEs")
print()
print(f"f(x, y) = {function_str}")
print(f"x0 = {x0}")
print(f"y0 = {y0}")
print(f"xn = {xn}")
print(f"Number of steps = {step}")
print()
if __name__ == "__main__":
main()
------------------------------------
x0 y0 yn
------------------------------------
0.0000 1.0000 1.2428
------------------------------------
0.2000 1.2428 1.5836
------------------------------------
0.4000 1.5836 2.0442
------------------------------------
0.6000 2.0442 2.6510
------------------------------------
0.8000 2.6510 3.4365
------------------------------------
1.0000 3.4365 4.4401
------------------------------------
1.2000 4.4401 5.7103
------------------------------------
1.4000 5.7103 7.3059
------------------------------------
1.6000 7.3059 9.2990
------------------------------------
1.8000 9.2990 11.7778
------------------------------------
At x=2.0000, y=11.7778
Test Case
Interval [x0 Initial Condition (x0 Step Size Approximate Solution (y at
ODE ,xend] ,y0) h xend) Exact Solution
dy/dx=x+y [0,2] y(0)=1 0.1 22.14171037 22.14170957
Exact not computed
dy/dx=x−y [0,1] y(0)=2 0.05 1.035213 analytically
2.Runge-Kutta fourth order method for system of ODEs / 2nd order ODE
Working Principle
The RK4 method is an iterative method for solving ordinary differential equations (ODEs). For a system of
ODEs or higher-order ODEs, they are reduced to a set of first-order ODEs, and the RK4 method is applied to
each equation simultaneously.
Pseudocode
Case A: System of ODEs
𝑑𝑦1
= 𝑓1(𝑥,𝑦1 ,𝑦2 ,…,𝑦𝑛 ) ,
𝑑𝑥
𝑑𝑦2
= 𝑓2(𝑥,𝑦1 ,𝑦2 ,…,𝑦𝑛 ) ,
𝑑𝑥
1
𝑦𝑖 = 𝑦𝑖 + ( +2 +2 + ),
6
1. Convert the second-order ODE into a system of two first-order ODEs. For example,
𝑑2 𝑦 𝑑𝑦
2
= 𝑓 (𝑥, 𝑦, ),
𝑑𝑥 𝑑𝑥
𝑑𝑦1
= 𝑦2 ,
𝑑𝑥
𝑑𝑦2
= 𝑓(𝑥, 𝑦1 , 𝑦2 ),
𝑑𝑥
2. Solve the system of ODEs using the RK4 method as outlined above.
import numpy as np
import matplotlib.pyplot as plt
x = x0
y1 = y10
y2 = y20
x_values.append(x)
y1_values.append(y1)
y2_values.append(y2)
# Inputs
x0 = 0 # Initial x
y10 = 1 # Initial y1
y20 = 0 # Initial y2
h = 0.1 # Step size
x_end = 10 # End point of x
# Visualization
plt.figure(figsize=(10, 6))
plt.plot(x_values, y1_values, 'b-', label="y1 (Displacement)")
plt.plot(x_values, y2_values, 'r--', label="y2 (Velocity)")
plt.title("Solution of System of ODEs using RK4 Method")
plt.xlabel("x")
plt.ylabel("y")
plt.legend()
plt.grid()
plt.show()
# Inputs
x0 = 0 # Initial x
y10 = 100 # Initial y1 (Position)
y20 = 0 # Initial y2 (Velocity)
h = 0.1 # Step size
x_end = 10 # End point of x
# Solve and visualize using the same RK4 function as above
x_values, y1_values, y2_values = rk4_system(f1, f2, x0, y10, y20, h,
x_end)
# Visualization
plt.figure(figsize=(10, 6))
plt.plot(x_values, y1_values, 'b-', label="y1 (Position)")
plt.plot(x_values, y2_values, 'r--', label="y2 (Velocity)")
plt.title("Solution of Second-Order ODE using RK4 Method")
plt.xlabel("x")
plt.ylabel("y")
plt.legend()
plt.grid()
plt.show()
import sympy as sp
def f(x,y):
return (y**2-x**2)/(y**2+x**2)
def func_input():
function_str = input("Enter your function (use 'x' and 'y' as the
variables) (Example: (y**2 - x**2)/(y**2 + x**2)): ")
x, y = sp.symbols('x y')
sp_function = sp.sympify(function_str)
func = sp.lambdify((x, y), sp_function, modules=['numpy'])
return func, sp_function
print('---------'*4)
print('x0\ty0\tyn')
print('---------'*4)
for i in range(n):
k1 = h * func(x0, y0)
k2 = h * func(x0 + h / 2, y0 + k1 / 2)
k3 = h * func(x0 + h / 2, y0 + k2 / 2)
k4 = h * func(x0 + h, y0 + k3)
k = (k1 + 2 * k2 + 2 * k3 + k4) / 6
yn = y0 + k
print(f'{x0:.4f}\t{y0:.4f}\t{yn:.4f}')
print('---------'*4)
x0 += h
y0 = yn
def main():
print("Runge-Kutta 4th Order (RK-4) Method for Solving ODEs")
print()
------------------------------------
x0 y0 yn
------------------------------------
0.0000 1.0000 1.1960
------------------------------------
0.2000 1.1960 1.3753
------------------------------------
0.4000 1.3753 1.5331
------------------------------------
0.6000 1.5331 1.6691
------------------------------------
0.8000 1.6691 1.7839
------------------------------------
1.0000 1.7839 1.8781
------------------------------------
1.2000 1.8781 1.9521
------------------------------------
1.4000 1.9521 2.0064
------------------------------------
1.6000 2.0064 2.0412
------------------------------------
1.8000 2.0412 2.0565
------------------------------------
𝑑2𝑦
𝑑𝑥 2
=f(x,y,y′)
with boundary conditions:
y(a)=α,y(b)=β
Approach
1. Convert the second-order ODE into a system of two first-order ODEs:
𝑑𝑦1 𝑑𝑦
𝑑𝑥
=y2, 𝑑𝑥2 =f(x,y1,y2)
2. Solve this system using an initial guess for y′(a)=y2(a) (denoted as s).
3. Use numerical integration (e.g., Runge-Kutta) to compute y(b) for the guessed s.
4. Adjust s iteratively (e.g., using Newton's method or the secant method) to ensure the computed y(b)
matches the boundary condition y(b)=β.
Pseudocode
1. Input:
𝑑2 𝑦
Define the ODE as 𝑑𝑥 2 =f(x,y,y′).
o Specify boundary conditions y(a)=α,y(b)=β
o Set the initial guess for s=y′(a).
2. Convert to a system of first-order ODEs:
o y1′=y2 ,
o y2′=f(x,y1,y2).
3. Iterative Procedure:
1. Solve the system using RK4 or other numerical methods for the current guess of s.
2. Compute the value of y(b)
3. Compare y(b)with the target boundary condition β:
▪ If y(b)) is close to β, stop.
▪ Otherwise, update s using:
𝑦(𝑏) − β
▪ 𝑠𝑛𝑒𝑤 = 𝑠𝑜𝑙𝑑 − 𝑆𝑙𝑜𝑝𝑒 𝑎𝑡 𝑠
x = x0
y1 = y10
y2 = y20
x_values.append(x)
y1_values.append(y1)
y2_values.append(y2)
# Shooting Method
def shooting_method(f, x0, x_end, y0, y_end, h, s_guess):
def boundary_condition_error(s):
# Solve the system for a given initial slope (s)
_, y1_values, _ = rk4_system(f, x0, y0, s, h, x_end)
return y1_values[-1] - y_end # Difference between computed
and target y(b)
# Inputs
x0 = 0 # Starting x
x_end = 2 # Ending x
y0 = 1 # Boundary condition y(a) = alpha
y_end = 2 # Boundary condition y(b) = beta
h = 0.1 # Step size
s_guess = 0 # Initial guess for y'(a)
# Visualization
plt.figure(figsize=(10, 6))
plt.plot(x_values, y_values, 'b-', label="Shooting Method Solution")
plt.title("Solution of Two-Point Boundary Value Problem using Shooting
Method")
plt.xlabel("x")
plt.ylabel("y")
plt.legend()
plt.grid()
plt.show()
4.Solution of two-point boundary value problem using finite difference method
Working Principle :The Finite Difference Method (FDM) solves two-point boundary value problems
(BVPs) by discretizing the differential equation into a system of linear algebraic equations. These
equations are then solved numerically to obtain the solution at discrete points.
Boundary Value Problem (BVP)
A second-order ODE:
𝑑2𝑦
𝑑𝑥 2
=f(x,y,y′)
a≤x≤b
with boundary conditions:
y(a)=α,y(b)=β
Finite Difference Approximation
1. Discretize the domain into n+1n+1n+1 equally spaced points:
𝑏−𝑎
with step size h=
𝑛
Pseudocode
1. Input:
𝑑2 𝑦
oDefine the second-order ODE as 𝑑𝑥 2 =f(x,y,y′)
o Specify boundary conditions y(a)=α,y(b)=β.
o Choose the number of grid points n and compute the step size h.
2. Discretize the domain:
o Divide [a,b] into n+1 points: x0,x1,…,xn
3. Construct the system of equations:
𝑑2 𝑦
oFor each interior point xi, use the finite difference approximation for 𝑑𝑥 2 .
o Form a tridiagonal matrix representing the system of linear equations.
4. Solve the linear system: Use a numerical solver like numpy.linalg.solve() to find yiy_iyi values at the
grid points.
5. Output: The solution y(x) at all grid points.
import numpy as np
import matplotlib.pyplot as plt
Parameters:
a, b: Endpoints of the interval [a, b]
alpha, beta: Boundary conditions y(a) = alpha, y(b) = beta
n: Number of interior points (grid points = n+1)
Returns:
x: Array of grid points
y: Array of solution values at the grid points
"""
h = (b - a) / (n + 1) # Step size
x = np.linspace(a, b, n + 2) # Grid points including boundaries
return x, y
# Inputs
a = 0 # Left boundary x=a
b = np.pi # Right boundary x=b
alpha = 0 # Boundary condition y(a) = 0
beta = 1 # Boundary condition y(b) = 1
n = 10 # Number of interior points
y_exact = exact_solution(x)
# Print results
print("Grid points (x):", x)
print("FDM solution (y):", y)
print("Exact solution (if available):", y_exact)
# Visualization
plt.figure(figsize=(8, 6))
plt.plot(x, y, 'o-', label="FDM Solution", color="blue")
plt.plot(x, y_exact, 'r--', label="Exact Solution", color="red")
plt.title("Solution of Two-Point Boundary Value Problem using FDM")
plt.xlabel("x")
plt.ylabel("y")
plt.legend()
plt.grid()
plt.show()
Working principle
𝜕2𝑢 𝜕2𝑢
+ = 0
𝜕𝑥 2 𝜕𝑦 2
It describes steady-state heat conduction, electrostatic potentials, and other equilibrium phenomena.
Pseudocode
1. Input:
o Domain size [xmin,xmax][ymin,ymax].
o Boundary conditions u(x,y)at the edges.
o Grid resolution (nx ,ny).
o Convergence criterion ϵ.
2. Discretize the domain:
o Create a grid with spacing hx, hy.
o Initialize grid values u(x,y), satisfying boundary conditions.
3. Gauss-Seidel Iteration:
o For each interior grid point (i,j), update:
import numpy as np
import matplotlib.pyplot as plt
Parameters:
domain: tuple (xmin, xmax, ymin, ymax) defining the problem domain
boundary_conditions: dictionary with keys "top", "bottom", "left",
"right" specifying boundary values
nx, ny: number of grid points along x and y axes
tol: convergence tolerance
Returns:
u: 2D array of solution values
x, y: grid points
"""
# Unpack domain
xmin, xmax, ymin, ymax = domain
hx = (xmax - xmin) / (nx - 1)
hy = (ymax - ymin) / (ny - 1)
# Create grid
x = np.linspace(xmin, xmax, nx)
y = np.linspace(ymin, ymax, ny)
u = np.zeros((ny, nx))
# Check convergence
error = np.max(np.abs(u - u_old))
if error < tol:
print(f"Converged after {iteration} iterations with error
{error}")
break
return x, y, u
plt.figure(figsize=(10, 6))
plt.contourf(X, Y, u, levels=50, cmap="viridis")
plt.colorbar(label="u(x, y)")
plt.title("Solution of Laplace Equation (Contour Plot)")
plt.xlabel("x")
plt.ylabel("y")
plt.show()
𝜕2𝑢 𝜕2𝑢
+ = 𝑓(𝑥, 𝑦)
𝜕𝑥 2 𝜕𝑦 2
where u(x,y) is the unknown function, and f(x,y) is the source term that represents the distribution of
sources or sinks..
Use the above formula iteratively to update the value of 𝑢𝑖,𝑗 at each grid point until the solution
converges.
Convergence is achieved when the maximum difference between successive iterations is less than
a specified tolerance (ϵ\epsilonϵ).
Pseudocode
6. Input:
o Domain size [xmin,xmax][ymin,ymax].
o Boundary conditions u(x,y)at the edges.
o Source term f(x,y)
o Grid resolution (nx ,ny).
o Convergence criterion ϵ.
7. Discretize the domain:
o Create a grid with spacing hx, hy.
o Initialize grid values u(x,y), satisfying boundary conditions.
8. Gauss-Seidel Iteration:
o For each interior grid point (i,j), update:
𝑘+1
𝑢𝑖+1,𝑗 𝑘 + 𝑢𝑖−1,𝑗 𝑘+1 + 𝑢𝑖,𝑗+1 𝑘 + 𝑢𝑖,𝑗−1 𝑘+1 − ℎ2 𝑓𝑖,𝑗
𝑢𝑖,𝑗 =
4
import numpy as np
import matplotlib.pyplot as plt
Parameters:
domain: tuple (xmin, xmax, ymin, ymax) defining the problem domain
boundary_conditions: dictionary with keys "top", "bottom", "left",
"right" specifying boundary values
f: function representing the source term f(x, y)
nx, ny: number of grid points along x and y axes
tol: convergence tolerance
Returns:
u: 2D array of solution values
x, y: grid points
"""
# Unpack domain
xmin, xmax, ymin, ymax = domain
hx = (xmax - xmin) / (nx - 1)
hy = (ymax - ymin) / (ny - 1)
h = hx # Assuming uniform grid spacing
# Create grid
x = np.linspace(xmin, xmax, nx)
y = np.linspace(ymin, ymax, ny)
u = np.zeros((ny, nx))
# Check convergence
error = np.max(np.abs(u - u_old))
if error < tol:
print(f"Converged after {iteration} iterations with error
{error}")
break
return x, y, u
# Visualization
X, Y = np.meshgrid(x, y)
plt.figure(figsize=(10, 6))
plt.contourf(X, Y, u, levels=50, cmap="viridis")
plt.colorbar(label="u(x, y)")
plt.title("Solution of Poisson's Equation (Contour Plot)")
plt.xlabel("x")
plt.ylabel("y")
plt.show()
𝜕𝑢 𝜕2 𝑢
𝜕𝑡
= α 𝜕𝑦2 where:
The Bendre-Schmidt method is a specific finite difference approach used to solve the heat equation. It
involves discretizing both the time and space domains using explicit schemes.
The method leads to an update formula for the temperature at the next time step 𝑢𝑖 𝑛+1 :
αΔt
• λ= 2
Δx
• 𝑢𝑖 𝑛 is the temperature at grid point i and time step n.
Pseudocode:
Input:
Discretization:
Initialization:
Time-stepping:
• For each time step nnn, update the temperature profile using the Bendre-Schmidt update rule:
𝑢𝑖 𝑛+1 = 𝑢𝑖 𝑛 + λ(𝑢𝑖−1 𝑛 − 2𝑢𝑖 𝑛 + 𝑢𝑖+1 𝑛 )
Output:
import numpy as np
import matplotlib.pyplot as plt
# Parameters
L = 10 # Length of the rod
Tmax = 2 # Total time
Nx = 50 # Number of spatial steps
Nt = 200 # Number of time steps
alpha = 0.01 # Thermal diffusivity
# Discretization
dx = L / (Nx - 1)
dt = Tmax / (Nt - 1)
# Bendre-Schmidt method
for n in range(0, Nt-1):
for i in range(1, Nx-1):
u[n+1, i] = u[n, i] + alpha * dt / dx**2 * (u[n, i+1] - 2*u[n,
i] + u[n, i-1])
plt.colorbar(label='Temperature (u)')
plt.title("Heat Distribution in the Rod Over Time (Bendre-Schmidt
Method)")
plt.xlabel("Time (t)")
plt.ylabel("Position (x)")
plt.show()
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# Parameters
L = 10 # Length of the rod
Tmax = 2 # Total time
Nx = 50 # Number of spatial steps
Nt = 200 # Number of time steps
alpha = 0.01 # Thermal diffusivity
# Discretization
dx = L / (Nx - 1)
dt = Tmax / (Nt - 1)
# 3D Surface Plot
X, T = np.meshgrid(x, t) # Create meshgrid for plotting
fig = plt.figure(figsize=(10, 6))
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(X, T, u, cmap='inferno') # 3D Surface plot
ax.set_title("Heat Distribution in the Rod Over Time (3D Surface
Plot)")
ax.set_xlabel("Position (x)")
ax.set_ylabel("Time (t)")
ax.set_zlabel("Temperature (u)")
plt.show()
4.One-dimensional heat equation using Crank- Nicholson method
Working principle
The one-dimensional heat equation is given as:
𝜕𝑢(𝑥,𝑡) 𝜕2 𝑢(𝑥,𝑡)
𝜕𝑡
=α 𝜕𝑦 2
where:
Crank-Nicholson Method
The Crank-Nicholson method is an implicit finite difference method that is numerically stable and accurate.
It is a combination of the forward Euler method (in time) and the central difference method (in space),
making it second-order accurate both in space and time.
𝑛+1
1 𝑛+1 𝑛 𝛼 𝑢𝑖+1 − 2𝑢𝑖𝑛+1 + 𝑢𝑖−1
𝑛+1 𝑛
𝑢𝑖+1 − 2𝑢𝑖𝑛 + 𝑢𝑖−1
𝑛
(𝑢 − 𝑢𝑖 ) = ( + )
Δ𝑡 𝑖 2 Δ𝑥 2 Δ𝑥 2
Where:
• 𝑢𝑖𝑛 is the value of the solution at spatial grid point iii and time step nnn.
• Δt is the time step, and Δx is the spatial grid spacing.
The method involves iterating over time steps and solving the system of equations for each time step to
evolve the temperature distribution over time.
Pseudocode:
1. Input:
– Compute the Crank-Nicholson finite difference scheme for all interior points.
– Solve the resulting tridiagonal system using Gaussian elimination or other
methods.
5. Output: The temperature distribution at each time step.
6. Visualization:
import numpy as np
import matplotlib.pyplot as plt
Parameters:
- L: length of the rod
- T: total time
- alpha: thermal diffusivity constant
- nx: number of spatial grid points
- nt: number of time steps
- u_initial: initial temperature distribution
- boundary_conditions: boundary conditions at x=0 and x=L
Returns:
- x: spatial grid points
- t: time points
- u: solution array (temperature distribution over time and space)
"""
# Discretize the space and time
dx = L / (nx - 1) # space step
dt = T / (nt - 1) # time step
r = alpha * dt / (2 * dx**2) # Crank-Nicholson parameter
# Problem parameters
L = 1.0 # length of the rod
T = 0.5 # total time
alpha = 0.01 # thermal diffusivity
nx = 10 # number of spatial points
nt = 100 # number of time steps
• Problem Setup:
o Length of rod L=1.0
o Total time T=0.5
o Thermal diffusivity α=0.01
o Grid size nx=10, nt=100
o Initial temperature distribution: A hot spot in the middle of the rod.
o Boundary conditions: u(0,t)=0, u(L,t)=0
• Expected Results:
o The temperature in the middle of the rod will decrease over time.
o Boundary points will remain at zero temperature.
Test Case 2:
• Problem Setup:
o Length of rod L=2.0
o Total time T=1.0
o Thermal diffusivity α=0.02
o Grid size nx=20, nt=200
o Initial temperature distribution: Heat distributed across the rod.
o Boundary conditions: u(0,t)=0, u(L,t)=0
• Expected Results:
o Temperature distribution will evolve, and heat will dissipate over time.