Develop a solver for tridiagonal linear systems of equations using C++ and integrate it with Python through pybind11. This exercise involves writing code to describe a problem that needs solving and implementing the linear solver.
Consider a linear system of the form
Thomas algorithm, also known as the tridiagonal matrix algorithm (TDMA), is a simplified form of Gaussian elimination that is specifically designed to efficiently solve tridiagonal systems of equations.
A tridiagonal system is one in which each row of the matrix A
involves at most three elements: one on the diagonal, one immediately above the diagonal (superdiagonal), and one immediately below the diagonal (subdiagonal).
Consider a tridiagonal system of n
linear equations, whose
a[i]*u[i-1] + b[i]*u[i] + c[i]*u[i+1] = f[i], for i = 0, 1, ..., n-1,
where:
-
a[i]
is the subdiagonal element, witha[0]
being undefined, -
b[i]
is the diagonal element, -
c[i]
is the superdiagonal element, withc[n-1]
being undefined, -
u[i]
is the$i$ -th element of the unknown vector, -
f[i]
is the$i$ -th element of the right-hand side vector.
The goal is to find the solution vector u
.
The steps of the Thomas algorithm are:
// Step 1.
for i = 1 to n-1:
m = a[i] / b[i-1]
b[i] = b[i] - m * c[i-1]
f[i] = f[i] - m * f[i-1]
// Step 2.
u[n-1] = f[n-1] / b[n-1]
for i = n-1 down to 1:
u[i-1] = (f[i-1] - c[i-1] * u[i]) / b[i-1]
The following equation can be used to model temperature diffusion in a 1D slab:
where
Consider a domain
By dividing the domain into
where
The above linear system is tridiagonal, thus it can be solved using the Thomas algorithm.
For a concrete example involving the heat equation, consider the following data:
-
(5 points) Implement an abstract
Matrix
class:- Implement an abstract
Matrix
class in C++, defining common matrix attributes and (possibly pure virtual) methods, such as getting the number of rows and columns, accessing elements, printing, and whatever functionality you find relevant using class methods and/or friend functions. - Include a pure virtual method
std::vector<double> solve(const std::vector<double> &f)
that solves a linear system given a right-hand side$f$ and returns its solution. - Derive a
TridiagonalMatrix
class fromMatrix
. Implement proper constructors to initialize it given the diagonal vectorsa
,b
, andc
. Override thesolve
method to implement the Thomas algorithm. - Check size compatibility via exception handling.
- Implement an abstract
-
(3 points) Solve the heat diffusion problem:
- Create a class
HeatDiffusion
representing the heat diffusion problem and exposing its relevant parameters (domain, boundary conditions, forcing term$f$ ). - Use the
TridiagonalMatrix
class to solve the heat equation from a givenHeatDiffusion
object instance. The wayHeatDiffusion
andTridiagonalMatrix
classes interact (e.g., inheritance, composition, ...) is up to you. Explain your design choices. - Validate the solution against the exact solution by computing the error
$\left|u - u_\mathrm{ex}\right|$ in Euclidean norm to assess the correctness of the implementation.
- Create a class
-
(2 points) Configuration and compilation
- Develop a CMake script for easy compilation of the C++ library.
- Provide clear instructions on compiling the library.
-
(5 points) Python bindings using pybind11:
- Bind the C++ functions, classes and their methods to Python, properly handling exceptions.
- Ensure the Python interface is user-friendly and adheres to Python conventions.
- Write a Python script that solves the heat equation using the provided C++ solver exposed through pybind11. Plot the numerical and exact solutions
$u(x)$ and$u_\mathrm{ex}(x)$ vs.$x$ for visual comparison. - Validate your implementation against the
solve
function fromnumpy.linalg
and/or against thesolve_banded
function fromscipy.linalg
.
-
(Bonus)
ThomasSolver
class development:- Use muParserX in
HeatDiffusion
to parse the functions$f(x)$ and$u_\mathrm{ex}(x)$ from input strings. - Define
ThomasSolver
as a template class to allow it to work with any matrix type that supports the necessary operations (accessing elements, etc.). The template parameter represents the matrix type. - Implement the Thomas algorithm within the
ThomasSolver
class, making sure the implementation does not assume anything specific about the matrix type beyond the necessary operations. - Instantiate the
ThomasSolver
class withTridiagonalMatrix
and with Eigen'sSparseMatrixXd
class. - Use
setuptools
to setup the build process for the Python bindings. - Write tests for both the C++ and the Python implementation.
- Use muParserX in