summaryrefslogtreecommitdiff
path: root/doc/software_manual.org
diff options
context:
space:
mode:
Diffstat (limited to 'doc/software_manual.org')
-rw-r--r--doc/software_manual.org243
1 files changed, 202 insertions, 41 deletions
diff --git a/doc/software_manual.org b/doc/software_manual.org
index 981d863..eebcf17 100644
--- a/doc/software_manual.org
+++ b/doc/software_manual.org
@@ -5,7 +5,7 @@
#+STARTUP: entitiespretty fold inlineimages
* Design
-The LIZFCM static library (at [[https://github.com/Simponic/math-4610][[https://github.com/Simponic/math-4610]] is a successor to my
+The LIZFCM static library (at [[https://github.com/Simponic/math-4610]]) is a successor to my
attempt at writing codes for the Fundamentals of Computational Mathematics course in Common
Lisp, but the effort required to meet the requirement of creating a static library became
too difficult to integrate outside of the ~ASDF~ solution that Common Lisp already brings
@@ -14,13 +14,14 @@ to the table.
All of the work established in ~deprecated-cl~ has been painstakingly translated into
the C programming language. I have a couple tenets for its design:
-+ Implemntations of routines should all be done immutably in respect to arguments.
++ Implementations of routines should all be done immutably in respect to arguments.
+ Functional programming is good (it's... rough in C though).
-+ Routines are separated into "module" c files, and not individual files per function.
++ Routines are separated into "modules" that follow a form of separation of concerns
+ in files, and not individual files per function.
* Compilation
-A provided ~Makefile~ is added for convencience. It has been tested on an M1 machine running MacOS as
-well as Arch Linux.
+A provided ~Makefile~ is added for convencience. It has been tested on an ~arm~-based M1 machine running
+MacOS as well as ~x86~ Arch Linux.
1. ~cd~ into the root of the repo
2. ~make~
@@ -317,6 +318,39 @@ void free_vector(Array_double *v) {
}
#+END_SRC
+*** ~add_element~
++ Author: Elizabeth Hunt
++ Name: ~add_element~
++ Location: ~src/vector.c~
++ Input: a pointer to an ~Array_double~
++ Output: a new ~Array_double~ with element ~x~ appended.
+
+#+BEGIN_SRC c
+Array_double *add_element(Array_double *v, double x) {
+ Array_double *pushed = InitArrayWithSize(double, v->size + 1, 0.0);
+ for (size_t i = 0; i < v->size; ++i)
+ pushed->data[i] = v->data[i];
+ pushed->data[v->size] = x;
+ return pushed;
+}
+#+END_SRC
+
+*** ~slice_element~
++ Author: Elizabeth Hunt
++ Name: ~slice_element~
++ Location: ~src/vector.c~
++ Input: a pointer to an ~Array_double~
++ Output: a new ~Array_double~ with element ~x~ sliced.
+
+#+BEGIN_SRC c
+Array_double *slice_element(Array_double *v, size_t x) {
+ Array_double *sliced = InitArrayWithSize(double, v->size - 1, 0.0);
+ for (size_t i = 0; i < v->size - 1; ++i)
+ sliced->data[i] = i >= x ? v->data[i + 1] : v->data[i];
+ return sliced;
+}
+#+END_SRC
+
*** ~copy_vector~
+ Author: Elizabeth Hunt
+ Name: ~copy_vector~
@@ -370,55 +404,54 @@ void format_vector_into(Array_double *v, char *s) {
matrix $L$, $U$, respectively such that $LU = m$.
+ Output: a pointer to the location in memory in which two ~Matrix_double~'s reside: the first
representing $L$, the second, $U$.
-+ Errors: Exits and throws a status code of ~-1~ when encountering a matrix that cannot be
++ Errors: Fails assertions when encountering a matrix that cannot be
decomposed
#+BEGIN_SRC c
- Matrix_double **lu_decomp(Matrix_double *m) {
- assert(m->cols == m->rows);
+Matrix_double **lu_decomp(Matrix_double *m) {
+ assert(m->cols == m->rows);
- Matrix_double *u = copy_matrix(m);
- Matrix_double *l_empt = InitMatrixWithSize(double, m->rows, m->cols, 0.0);
- Matrix_double *l = put_identity_diagonal(l_empt);
- free(l_empt);
+ Matrix_double *u = copy_matrix(m);
+ Matrix_double *l_empt = InitMatrixWithSize(double, m->rows, m->cols, 0.0);
+ Matrix_double *l = put_identity_diagonal(l_empt);
+ free_matrix(l_empt);
+ Matrix_double **u_l = malloc(sizeof(Matrix_double *) * 2);
- Matrix_double **u_l = malloc(sizeof(Matrix_double *) * 2);
-
- for (size_t y = 0; y < m->rows; y++) {
- if (u->data[y]->data[y] == 0) {
- printf("ERROR: a pivot is zero in given matrix\n");
- exit(-1);
- }
+ for (size_t y = 0; y < m->rows; y++) {
+ if (u->data[y]->data[y] == 0) {
+ printf("ERROR: a pivot is zero in given matrix\n");
+ assert(false);
}
+ }
- if (u && l) {
- for (size_t x = 0; x < m->cols; x++) {
- for (size_t y = x + 1; y < m->rows; y++) {
- double denom = u->data[x]->data[x];
+ if (u && l) {
+ for (size_t x = 0; x < m->cols; x++) {
+ for (size_t y = x + 1; y < m->rows; y++) {
+ double denom = u->data[x]->data[x];
- if (denom == 0) {
- printf("ERROR: non-factorable matrix\n");
- exit(-1);
- }
+ if (denom == 0) {
+ printf("ERROR: non-factorable matrix\n");
+ assert(false);
+ }
- double factor = -(u->data[y]->data[x] / denom);
+ double factor = -(u->data[y]->data[x] / denom);
- Array_double *scaled = scale_v(u->data[x], factor);
- Array_double *added = add_v(scaled, u->data[y]);
- free_vector(scaled);
- free_vector(u->data[y]);
+ Array_double *scaled = scale_v(u->data[x], factor);
+ Array_double *added = add_v(scaled, u->data[y]);
+ free_vector(scaled);
+ free_vector(u->data[y]);
- u->data[y] = added;
- l->data[y]->data[x] = -factor;
- }
+ u->data[y] = added;
+ l->data[y]->data[x] = -factor;
}
}
-
- u_l[0] = u;
- u_l[1] = l;
- return u_l;
}
+
+ u_l[0] = u;
+ u_l[1] = l;
+ return u_l;
+}
#+END_SRC
*** ~bsubst~
+ Author: Elizabeth Hunt
@@ -467,7 +500,7 @@ Array_double *fsubst(Matrix_double *l, Array_double *b) {
}
#+END_SRC
-*** ~solve_matrix~
+*** ~solve_matrix_lu_bsubst~
+ Author: Elizabeth Hunt
+ Location: ~src/matrix.c~
+ Input: a pointer to a ~Matrix_double~ $m$ and a pointer to an ~Array_double~ $b$
@@ -480,7 +513,7 @@ Here we make use of forward substitution to first solve $Ly = b$ given $L$ as th
Then, $LUx = b$, thus $x$ is a solution.
#+BEGIN_SRC c
-Array_double *solve_matrix(Matrix_double *m, Array_double *b) {
+Array_double *solve_matrix_lu_bsubst(Matrix_double *m, Array_double *b) {
assert(b->size == m->rows);
assert(m->rows == m->cols);
@@ -495,11 +528,97 @@ Array_double *solve_matrix(Matrix_double *m, Array_double *b) {
free_matrix(u);
free_matrix(l);
+ free(u_l);
return x;
}
#+END_SRC
+*** ~gaussian_elimination~
++ Author: Elizabeth Hunt
++ Location: ~src/matrix.c~
++ Input: a pointer to a ~Matrix_double~ $m$
++ Output: a pointer to a copy of $m$ in reduced echelon form
+
+This works by finding the row with a maximum value in the column $k$. Then, it uses that as a pivot, and
+applying reduction to all other rows. The general idea is available at [[https://en.wikipedia.org/wiki/Gaussian_elimination]].
+
+#+BEGIN_SRC c
+Matrix_double *gaussian_elimination(Matrix_double *m) {
+ uint64_t h = 0;
+ uint64_t k = 0;
+
+ Matrix_double *m_cp = copy_matrix(m);
+
+ while (h < m_cp->rows && k < m_cp->cols) {
+ uint64_t max_row = 0;
+ double total_max = 0.0;
+
+ for (uint64_t row = h; row < m_cp->rows; row++) {
+ double this_max = c_max(fabs(m_cp->data[row]->data[k]), total_max);
+ if (c_max(this_max, total_max) == this_max) {
+ max_row = row;
+ }
+ }
+
+ if (max_row == 0) {
+ k++;
+ continue;
+ }
+
+ Array_double *swp = m_cp->data[max_row];
+ m_cp->data[max_row] = m_cp->data[h];
+ m_cp->data[h] = swp;
+
+ for (uint64_t row = h + 1; row < m_cp->rows; row++) {
+ double factor = m_cp->data[row]->data[k] / m_cp->data[h]->data[k];
+ m_cp->data[row]->data[k] = 0.0;
+
+ for (uint64_t col = k + 1; col < m_cp->cols; col++) {
+ m_cp->data[row]->data[col] -= m_cp->data[h]->data[col] * factor;
+ }
+ }
+
+ h++;
+ k++;
+ }
+
+ return m_cp;
+}
+#+END_SRC
+
+*** ~solve_matrix_gaussian~
++ Author: Elizabeth Hunt
++ Location: ~src/matrix.c~
++ Input: a pointer to a ~Matrix_double~ $m$ and a target ~Array_double~ $b$
++ Output: a pointer to a vector $x$ being the solution to the equation $mx = b$
+
+We first perform ~gaussian_elimination~ after augmenting $m$ and $b$. Then, as $m$ is in reduced echelon form, it's an upper
+triangular matrix, so we can perform back substitution to compute $x$.
+
+#+BEGIN_SRC c
+Array_double *solve_matrix_gaussian(Matrix_double *m, Array_double *b) {
+ assert(b->size == m->rows);
+ assert(m->rows == m->cols);
+
+ Matrix_double *m_augment_b = add_column(m, b);
+ Matrix_double *eliminated = gaussian_elimination(m_augment_b);
+
+ Array_double *b_gauss = col_v(eliminated, m->cols);
+ Matrix_double *u = slice_column(eliminated, m->rows);
+
+ Array_double *solution = bsubst(u, b_gauss);
+
+ free_matrix(m_augment_b);
+ free_matrix(eliminated);
+ free_matrix(u);
+ free_vector(b_gauss);
+
+ return solution;
+}
+#+END_SRC
+
+
*** ~m_dot_v~
+ Author: Elizabeth Hunt
+ Location: ~src/matrix.c~
@@ -535,6 +654,48 @@ Matrix_double *put_identity_diagonal(Matrix_double *m) {
}
#+END_SRC
+*** ~slice_column~
++ Author: Elizabeth Hunt
++ Location: ~src/matrix.c~
++ Input: a pointer to a ~Matrix_double~
++ Output: a pointer to a copy of the given ~Matrix_double~ with column at ~x~ sliced
+
+#+BEGIN_SRC c
+Matrix_double *slice_column(Matrix_double *m, size_t x) {
+ Matrix_double *sliced = copy_matrix(m);
+
+ for (size_t row = 0; row < m->rows; row++) {
+ Array_double *old_row = sliced->data[row];
+ sliced->data[row] = slice_element(old_row, x);
+ free_vector(old_row);
+ }
+ sliced->cols--;
+
+ return sliced;
+}
+#+END_SRC
+
+*** ~add_column~
++ Author: Elizabet Hunt
++ Location: ~src/matrix.c~
++ Input: a pointer to a ~Matrix_double~ and a new vector representing the appended column ~x~
++ Output: a pointer to a copy of the given ~Matrix_double~ with a new column ~x~
+
+#+BEGIN_SRC c
+Matrix_double *add_column(Matrix_double *m, Array_double *v) {
+ Matrix_double *pushed = copy_matrix(m);
+
+ for (size_t row = 0; row < m->rows; row++) {
+ Array_double *old_row = pushed->data[row];
+ pushed->data[row] = add_element(old_row, v->data[row]);
+ free_vector(old_row);
+ }
+
+ pushed->cols++;
+ return pushed;
+}
+#+END_SRC
+
*** ~copy_matrix~
+ Author: Elizabeth Hunt
+ Location: ~src/matrix.c~