diff options
| author | Elizabeth Alexander Hunt <me@liz.coffee> | 2026-07-02 11:55:17 -0700 |
|---|---|---|
| committer | Elizabeth Alexander Hunt <me@liz.coffee> | 2026-07-02 11:55:17 -0700 |
| commit | 6bf4b90c90f15f4ab60833bddf5b5756d1a6b1f6 (patch) | |
| tree | ed97e39ec77c5231ffd2c394493e68d00ddac5a4 /Homework/math4610 | |
| download | misc-undergrad-main.tar.gz misc-undergrad-main.zip | |
Diffstat (limited to 'Homework/math4610')
139 files changed, 10882 insertions, 0 deletions
diff --git a/Homework/math4610/Makefile b/Homework/math4610/Makefile new file mode 100644 index 0000000..09e44d2 --- /dev/null +++ b/Homework/math4610/Makefile @@ -0,0 +1,46 @@ +SRC_DIR := src +OBJ_DIR := build +BIN_DIR := dist +LIB_DIR := lib + +TEST_SRC_DIR := test +TEST_SRC := $(wildcard $(TEST_SRC_DIR)/*.c) +TEST_OBJ := $(TEST_SRC:$(TEST_SRC_DIR)/%.c=$(OBJ_DIR)/%.o) + +TEST_EXE := $(BIN_DIR)/lizfcm.test +EXE := $(BIN_DIR)/lizfcm +LIBRARY := $(LIB_DIR)/lizfcm.a +SRC := $(wildcard $(SRC_DIR)/*.c) +OBJ := $(SRC:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o) + +CPPFLAGS := -Iinc -MMD -MP +CFLAGS := -Wall +LDFLAGS := -lm + +.PHONY: all clean + +all: $(TEST_EXE) + +$(TEST_EXE): $(TEST_OBJ) $(LIBRARY) | $(BIN_DIR) + $(CC) $(CPPFLAGS) $(CFLAGS) $^ -o $@ $(LDFLAGS) + +$(LIBRARY): $(OBJ) | $(LIB_DIR) + ar rcs $(LIBRARY) $(OBJ_DIR)/*.o + ranlib $(LIBRARY) + +$(OBJ_DIR)/%.o: $(TEST_SRC_DIR)/%.c | $(OBJ_DIR) + $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ + +$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR) + $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ + +$(BIN_DIR) $(OBJ_DIR) $(LIB_DIR): + mkdir -p $@ + +print-% : ; @echo $* = $($*) + +clean: + @$(RM) -r $(BIN_DIR) $(OBJ_DIR) $(LIB_DIR) + +-include $(OBJ:.o=.d) +-include $(TEST_OBJ:.o=.d) diff --git a/Homework/math4610/build/approx_derivative.d b/Homework/math4610/build/approx_derivative.d new file mode 100644 index 0000000..ce40007 --- /dev/null +++ b/Homework/math4610/build/approx_derivative.d @@ -0,0 +1,8 @@ +build/approx_derivative.o: src/approx_derivative.c inc/lizfcm.h \ + inc/macros.h inc/types.h + +inc/lizfcm.h: + +inc/macros.h: + +inc/types.h: diff --git a/Homework/math4610/build/approx_derivative.o b/Homework/math4610/build/approx_derivative.o Binary files differnew file mode 100644 index 0000000..0eb579a --- /dev/null +++ b/Homework/math4610/build/approx_derivative.o diff --git a/Homework/math4610/build/approx_derivative.t.d b/Homework/math4610/build/approx_derivative.t.d new file mode 100644 index 0000000..a5cc002 --- /dev/null +++ b/Homework/math4610/build/approx_derivative.t.d @@ -0,0 +1,12 @@ +build/approx_derivative.t.o: test/approx_derivative.t.c \ + test/lizfcm.test.h inc/lizfcm.h inc/macros.h inc/types.h test/utest.h + +test/lizfcm.test.h: + +inc/lizfcm.h: + +inc/macros.h: + +inc/types.h: + +test/utest.h: diff --git a/Homework/math4610/build/approx_derivative.t.o b/Homework/math4610/build/approx_derivative.t.o Binary files differnew file mode 100644 index 0000000..32e1cf3 --- /dev/null +++ b/Homework/math4610/build/approx_derivative.t.o diff --git a/Homework/math4610/build/eigen.d b/Homework/math4610/build/eigen.d new file mode 100644 index 0000000..478e52c --- /dev/null +++ b/Homework/math4610/build/eigen.d @@ -0,0 +1,7 @@ +build/eigen.o: src/eigen.c inc/lizfcm.h inc/macros.h inc/types.h + +inc/lizfcm.h: + +inc/macros.h: + +inc/types.h: diff --git a/Homework/math4610/build/eigen.o b/Homework/math4610/build/eigen.o Binary files differnew file mode 100644 index 0000000..94245e6 --- /dev/null +++ b/Homework/math4610/build/eigen.o diff --git a/Homework/math4610/build/eigen.t.d b/Homework/math4610/build/eigen.t.d new file mode 100644 index 0000000..7a0ea06 --- /dev/null +++ b/Homework/math4610/build/eigen.t.d @@ -0,0 +1,12 @@ +build/eigen.t.o: test/eigen.t.c test/lizfcm.test.h inc/lizfcm.h \ + inc/macros.h inc/types.h test/utest.h + +test/lizfcm.test.h: + +inc/lizfcm.h: + +inc/macros.h: + +inc/types.h: + +test/utest.h: diff --git a/Homework/math4610/build/eigen.t.o b/Homework/math4610/build/eigen.t.o Binary files differnew file mode 100644 index 0000000..12730e7 --- /dev/null +++ b/Homework/math4610/build/eigen.t.o diff --git a/Homework/math4610/build/jacobi.t.d b/Homework/math4610/build/jacobi.t.d new file mode 100644 index 0000000..ee3573e --- /dev/null +++ b/Homework/math4610/build/jacobi.t.d @@ -0,0 +1,12 @@ +build/jacobi.t.o: test/jacobi.t.c test/lizfcm.test.h inc/lizfcm.h \ + inc/macros.h inc/types.h test/utest.h + +test/lizfcm.test.h: + +inc/lizfcm.h: + +inc/macros.h: + +inc/types.h: + +test/utest.h: diff --git a/Homework/math4610/build/jacobi.t.o b/Homework/math4610/build/jacobi.t.o Binary files differnew file mode 100644 index 0000000..b3feb59 --- /dev/null +++ b/Homework/math4610/build/jacobi.t.o diff --git a/Homework/math4610/build/lin.d b/Homework/math4610/build/lin.d new file mode 100644 index 0000000..f8eca56 --- /dev/null +++ b/Homework/math4610/build/lin.d @@ -0,0 +1,7 @@ +build/lin.o: src/lin.c inc/lizfcm.h inc/macros.h inc/types.h + +inc/lizfcm.h: + +inc/macros.h: + +inc/types.h: diff --git a/Homework/math4610/build/lin.o b/Homework/math4610/build/lin.o Binary files differnew file mode 100644 index 0000000..e34e656 --- /dev/null +++ b/Homework/math4610/build/lin.o diff --git a/Homework/math4610/build/lin.t.d b/Homework/math4610/build/lin.t.d new file mode 100644 index 0000000..20f8987 --- /dev/null +++ b/Homework/math4610/build/lin.t.d @@ -0,0 +1,12 @@ +build/lin.t.o: test/lin.t.c test/lizfcm.test.h inc/lizfcm.h inc/macros.h \ + inc/types.h test/utest.h + +test/lizfcm.test.h: + +inc/lizfcm.h: + +inc/macros.h: + +inc/types.h: + +test/utest.h: diff --git a/Homework/math4610/build/lin.t.o b/Homework/math4610/build/lin.t.o Binary files differnew file mode 100644 index 0000000..a235333 --- /dev/null +++ b/Homework/math4610/build/lin.t.o diff --git a/Homework/math4610/build/maceps.d b/Homework/math4610/build/maceps.d new file mode 100644 index 0000000..ef8daf4 --- /dev/null +++ b/Homework/math4610/build/maceps.d @@ -0,0 +1,7 @@ +build/maceps.o: src/maceps.c inc/lizfcm.h inc/macros.h inc/types.h + +inc/lizfcm.h: + +inc/macros.h: + +inc/types.h: diff --git a/Homework/math4610/build/maceps.o b/Homework/math4610/build/maceps.o Binary files differnew file mode 100644 index 0000000..265527a --- /dev/null +++ b/Homework/math4610/build/maceps.o diff --git a/Homework/math4610/build/maceps.t.d b/Homework/math4610/build/maceps.t.d new file mode 100644 index 0000000..d069a18 --- /dev/null +++ b/Homework/math4610/build/maceps.t.d @@ -0,0 +1,12 @@ +build/maceps.t.o: test/maceps.t.c test/lizfcm.test.h inc/lizfcm.h \ + inc/macros.h inc/types.h test/utest.h + +test/lizfcm.test.h: + +inc/lizfcm.h: + +inc/macros.h: + +inc/types.h: + +test/utest.h: diff --git a/Homework/math4610/build/maceps.t.o b/Homework/math4610/build/maceps.t.o Binary files differnew file mode 100644 index 0000000..025076e --- /dev/null +++ b/Homework/math4610/build/maceps.t.o diff --git a/Homework/math4610/build/main.d b/Homework/math4610/build/main.d new file mode 100644 index 0000000..9ff86d0 --- /dev/null +++ b/Homework/math4610/build/main.d @@ -0,0 +1,12 @@ +build/main.o: test/main.c test/lizfcm.test.h inc/lizfcm.h inc/macros.h \ + inc/types.h test/utest.h + +test/lizfcm.test.h: + +inc/lizfcm.h: + +inc/macros.h: + +inc/types.h: + +test/utest.h: diff --git a/Homework/math4610/build/main.o b/Homework/math4610/build/main.o Binary files differnew file mode 100644 index 0000000..d3a9735 --- /dev/null +++ b/Homework/math4610/build/main.o diff --git a/Homework/math4610/build/matrix.d b/Homework/math4610/build/matrix.d new file mode 100644 index 0000000..7c13130 --- /dev/null +++ b/Homework/math4610/build/matrix.d @@ -0,0 +1,7 @@ +build/matrix.o: src/matrix.c inc/lizfcm.h inc/macros.h inc/types.h + +inc/lizfcm.h: + +inc/macros.h: + +inc/types.h: diff --git a/Homework/math4610/build/matrix.o b/Homework/math4610/build/matrix.o Binary files differnew file mode 100644 index 0000000..c28fc55 --- /dev/null +++ b/Homework/math4610/build/matrix.o diff --git a/Homework/math4610/build/matrix.t.d b/Homework/math4610/build/matrix.t.d new file mode 100644 index 0000000..f707d56 --- /dev/null +++ b/Homework/math4610/build/matrix.t.d @@ -0,0 +1,12 @@ +build/matrix.t.o: test/matrix.t.c test/lizfcm.test.h inc/lizfcm.h \ + inc/macros.h inc/types.h test/utest.h + +test/lizfcm.test.h: + +inc/lizfcm.h: + +inc/macros.h: + +inc/types.h: + +test/utest.h: diff --git a/Homework/math4610/build/matrix.t.o b/Homework/math4610/build/matrix.t.o Binary files differnew file mode 100644 index 0000000..a3c56f6 --- /dev/null +++ b/Homework/math4610/build/matrix.t.o diff --git a/Homework/math4610/build/rand.d b/Homework/math4610/build/rand.d new file mode 100644 index 0000000..e3fef0e --- /dev/null +++ b/Homework/math4610/build/rand.d @@ -0,0 +1,7 @@ +build/rand.o: src/rand.c inc/lizfcm.h inc/macros.h inc/types.h + +inc/lizfcm.h: + +inc/macros.h: + +inc/types.h: diff --git a/Homework/math4610/build/rand.o b/Homework/math4610/build/rand.o Binary files differnew file mode 100644 index 0000000..97ea05a --- /dev/null +++ b/Homework/math4610/build/rand.o diff --git a/Homework/math4610/build/rand.t.d b/Homework/math4610/build/rand.t.d new file mode 100644 index 0000000..28a7d0f --- /dev/null +++ b/Homework/math4610/build/rand.t.d @@ -0,0 +1,12 @@ +build/rand.t.o: test/rand.t.c test/lizfcm.test.h inc/lizfcm.h \ + inc/macros.h inc/types.h test/utest.h + +test/lizfcm.test.h: + +inc/lizfcm.h: + +inc/macros.h: + +inc/types.h: + +test/utest.h: diff --git a/Homework/math4610/build/rand.t.o b/Homework/math4610/build/rand.t.o Binary files differnew file mode 100644 index 0000000..2c55eff --- /dev/null +++ b/Homework/math4610/build/rand.t.o diff --git a/Homework/math4610/build/roots.d b/Homework/math4610/build/roots.d new file mode 100644 index 0000000..666ae7b --- /dev/null +++ b/Homework/math4610/build/roots.d @@ -0,0 +1,7 @@ +build/roots.o: src/roots.c inc/lizfcm.h inc/macros.h inc/types.h + +inc/lizfcm.h: + +inc/macros.h: + +inc/types.h: diff --git a/Homework/math4610/build/roots.o b/Homework/math4610/build/roots.o Binary files differnew file mode 100644 index 0000000..f766021 --- /dev/null +++ b/Homework/math4610/build/roots.o diff --git a/Homework/math4610/build/roots.t.d b/Homework/math4610/build/roots.t.d new file mode 100644 index 0000000..833625f --- /dev/null +++ b/Homework/math4610/build/roots.t.d @@ -0,0 +1,12 @@ +build/roots.t.o: test/roots.t.c test/lizfcm.test.h inc/lizfcm.h \ + inc/macros.h inc/types.h test/utest.h + +test/lizfcm.test.h: + +inc/lizfcm.h: + +inc/macros.h: + +inc/types.h: + +test/utest.h: diff --git a/Homework/math4610/build/roots.t.o b/Homework/math4610/build/roots.t.o Binary files differnew file mode 100644 index 0000000..3457048 --- /dev/null +++ b/Homework/math4610/build/roots.t.o diff --git a/Homework/math4610/build/vector.d b/Homework/math4610/build/vector.d new file mode 100644 index 0000000..806d770 --- /dev/null +++ b/Homework/math4610/build/vector.d @@ -0,0 +1,7 @@ +build/vector.o: src/vector.c inc/lizfcm.h inc/macros.h inc/types.h + +inc/lizfcm.h: + +inc/macros.h: + +inc/types.h: diff --git a/Homework/math4610/build/vector.o b/Homework/math4610/build/vector.o Binary files differnew file mode 100644 index 0000000..8053730 --- /dev/null +++ b/Homework/math4610/build/vector.o diff --git a/Homework/math4610/build/vector.t.d b/Homework/math4610/build/vector.t.d new file mode 100644 index 0000000..32b1404 --- /dev/null +++ b/Homework/math4610/build/vector.t.d @@ -0,0 +1,12 @@ +build/vector.t.o: test/vector.t.c test/lizfcm.test.h inc/lizfcm.h \ + inc/macros.h inc/types.h test/utest.h + +test/lizfcm.test.h: + +inc/lizfcm.h: + +inc/macros.h: + +inc/types.h: + +test/utest.h: diff --git a/Homework/math4610/build/vector.t.o b/Homework/math4610/build/vector.t.o Binary files differnew file mode 100644 index 0000000..bca5a4d --- /dev/null +++ b/Homework/math4610/build/vector.t.o diff --git a/Homework/math4610/compile_flags.txt b/Homework/math4610/compile_flags.txt new file mode 100644 index 0000000..b47959f --- /dev/null +++ b/Homework/math4610/compile_flags.txt @@ -0,0 +1,5 @@ +-lm +-Iinc +-MMD +-MP +-Wall diff --git a/Homework/math4610/deprecated-cl/approx,derivative.lisp b/Homework/math4610/deprecated-cl/approx,derivative.lisp new file mode 100644 index 0000000..631a5c0 --- /dev/null +++ b/Homework/math4610/deprecated-cl/approx,derivative.lisp @@ -0,0 +1,25 @@ +(in-package :lizfcm.approx) + +(defun central-derivative-at (f x &optional (delta 0.01)) + (let* ((x2 (+ x delta)) + (x1 (- x delta)) + (y2 (apply f (list x2))) + (y1 (apply f (list x1)))) + (/ (- y2 y1) + (- x2 x1)))) + +(defun forward-derivative-at (f x &optional (delta 0.01)) + (let* ((x2 (+ x delta)) + (x1 x) + (y2 (apply f (list x2))) + (y1 (apply f (list x1)))) + (/ (- y2 y1) + (- x2 x1)))) + +(defun backward-derivative-at (f x &optional (delta 0.01)) + (let* ((x2 x) + (x1 (- x delta)) + (y2 (apply f (list x2))) + (y1 (apply f (list x1)))) + (/ (- y2 y1) + (- x2 x1)))) diff --git a/Homework/math4610/deprecated-cl/approx,maceps.lisp b/Homework/math4610/deprecated-cl/approx,maceps.lisp new file mode 100644 index 0000000..e2738e4 --- /dev/null +++ b/Homework/math4610/deprecated-cl/approx,maceps.lisp @@ -0,0 +1,12 @@ +(in-package :lizfcm.approx) + +(defun compute-maceps (f a init) + (let ((h init) + (err init)) + (loop collect (list a h err) + do + (setf h (/ h 2) + err (abs (- (funcall f (+ a h)) + (funcall f a)))) + while (> err 0)))) + diff --git a/Homework/math4610/deprecated-cl/approx,package.lisp b/Homework/math4610/deprecated-cl/approx,package.lisp new file mode 100644 index 0000000..a0eac80 --- /dev/null +++ b/Homework/math4610/deprecated-cl/approx,package.lisp @@ -0,0 +1,7 @@ +(in-package :cl-user) +(defpackage lizfcm.approx + (:use :cl) + (:export :central-derivative-at + :forward-derivative-at + :backward-derivative-at + :compute-maceps)) diff --git a/Homework/math4610/deprecated-cl/lizfcm.asd b/Homework/math4610/deprecated-cl/lizfcm.asd new file mode 100644 index 0000000..dea3ddd --- /dev/null +++ b/Homework/math4610/deprecated-cl/lizfcm.asd @@ -0,0 +1,33 @@ +(asdf:defsystem "lizfcm" + :version "0.1.0" + :author "Elizabeth Hunt" + :license "MIT" + :components + ((:file "utils,within-range" :depends-on ("utils,package")) + (:file "utils,table" :depends-on ("utils,package")) + (:file "utils,package") + (:file "approx,maceps" :depends-on ("approx,package")) + (:file "approx,derivative" :depends-on ("approx,package")) + (:file "approx,package") + (:file "vector,least-squares" :depends-on ("vector,package")) + (:file "vector,distance" :depends-on ("vector,norm" "vector,package")) + (:file "vector,norm" :depends-on ("vector,package")) + (:file "vector,package"))) + + +(asdf:defsystem "lizfcm/tests" + :author "Elizabeth Hunt" + :license "MIT" + :depends-on + (:fiveam + :lizfcm) + :components + ((:file "tests,table" :depends-on ("tests,suite")) + (:file "tests,maceps" :depends-on ("tests,suite")) + (:file "tests,approx" :depends-on ("tests,suite")) + (:file "tests,vector" :depends-on ("tests,suite")) + (:file "tests,suite")) + :perform + (asdf:test-op (o c) (uiop:symbol-call + :fiveam :run! + (uiop:find-symbol* :lizfcm-test-suite :lizfcm/tests)))) diff --git a/Homework/math4610/deprecated-cl/main.lisp b/Homework/math4610/deprecated-cl/main.lisp new file mode 100644 index 0000000..7a8b455 --- /dev/null +++ b/Homework/math4610/deprecated-cl/main.lisp @@ -0,0 +1,60 @@ +(load "lizfcm.asd") +(ql:quickload :lizfcm) + +;; this is a collection showcasing the library developed for math4610, required +;; from the Shared Library definition + +(defun smaceps () + (cadar (last (lizfcm.approx:compute-maceps + (lambda (x) x) 1.0 1.0)))) + +(defun dmaceps () + (cadar (last (lizfcm.approx:compute-maceps + (lambda (x) x) 1.0d0 1.0d0)))) + +(defun l2-norm (v) + (let ((2-norm (lizfcm.vector:p-norm 2))) + (funcall 2-norm v))) + +(defun l1-norm (v) + (let ((1-norm (lizfcm.vector:p-norm 1))) + (funcall 1-norm v))) + +(defun linf-norm (v) + (lizfcm.vector:max-norm v)) + +(defun l2-distance (v1 v2) + (let ((2-norm (lizfcm.vector:p-norm 2))) + (lizfcm.vector:distance v1 v2 2-norm))) + +(defun l1-distance (v1 v2) + (let ((1-norm (lizfcm.vector:p-norm 1))) + (lizfcm.vector:distance v1 v2 1-norm))) + +(defun linf-distance (v1 v2) + (lizfcm.vector:distance v1 v2 'lizfcm.vector:max-norm)) + +(defun f (x) + (/ (- x 1) (+ x 1))) + +(defun fprime (x) + (/ 2 (expt (+ x 1) 2))) + +(defmacro showcase (s-expr) + `(format t "~a = ~a~%" ,(format nil "~a" s-expr) ,s-expr)) + +(defun main () + (showcase (smaceps)) + (showcase (dmaceps)) + (showcase (l2-norm '(1 2))) + (showcase (l1-norm '(1 2))) + (showcase (linf-norm '(1 2))) + (showcase (l1-distance '(1 2) '(-3 4))) + (showcase (l2-distance '(1 2) '(-3 4))) + (showcase (linf-distance '(1 2) '(-3 4))) + (showcase (lizfcm.vector:least-squares-reg '(1 2 3 4 5 6 7) + '(0.5 3 2 3.5 5 6 7.5))) + (showcase (lizfcm.approx:forward-derivative-at 'f 1 0.00001)) + (showcase (lizfcm.approx:central-derivative-at 'f 1 0.00001)) + (showcase (lizfcm.approx:backward-derivative-at 'f 1 0.00001))) + diff --git a/Homework/math4610/deprecated-cl/tests,approx.lisp b/Homework/math4610/deprecated-cl/tests,approx.lisp new file mode 100644 index 0000000..678ff8c --- /dev/null +++ b/Homework/math4610/deprecated-cl/tests,approx.lisp @@ -0,0 +1,48 @@ +(defpackage lizfcm/tests.approx + (:use :cl + :fiveam + :lizfcm.approx + :lizfcm.utils + :lizfcm/tests) + (:export :approx-suite)) +(in-package :lizfcm/tests.approx) + +(def-suite approx-suite + :in lizfcm-test-suite) +(in-suite approx-suite) + +(test central-derivative-at + :description "derivative at is within bounds" + (let ((f (lambda (x) (* x x))) + (x 2) + (accepted-delta 0.02) + (f-prime-at-x 4) + (delta 0.01)) + (is (within-range-p + (central-derivative-at f x delta) + f-prime-at-x + accepted-delta)))) + +(test fwd-derivative-at + :description "forward derivative at is within bounds" + (let ((f (lambda (x) (* x x))) + (x 2) + (accepted-delta 0.02) + (f-prime-at-x 4) + (delta 0.01)) + (is (within-range-p + (forward-derivative-at f x delta) + f-prime-at-x + accepted-delta)))) + +(test bwd-derivative-at + :description "backward derivative at is within bounds" + (let ((f (lambda (x) (* x x))) + (x 2) + (accepted-delta 0.02) + (f-prime-at-x 4) + (delta 0.01)) + (is (within-range-p + (backward-derivative-at f x delta) + f-prime-at-x + accepted-delta)))) diff --git a/Homework/math4610/deprecated-cl/tests,maceps.lisp b/Homework/math4610/deprecated-cl/tests,maceps.lisp new file mode 100644 index 0000000..cd5ced9 --- /dev/null +++ b/Homework/math4610/deprecated-cl/tests,maceps.lisp @@ -0,0 +1,27 @@ +(defpackage lizfcm/tests.maceps + (:use :cl + :fiveam + :lizfcm.approx + :lizfcm.utils + :lizfcm/tests) + (:export :approx-suite)) +(in-package :lizfcm/tests.maceps) + +(def-suite maceps-suite + :in lizfcm-test-suite) +(in-suite maceps-suite) + +(test maceps + :description "double precision provides precision about (mac eps of single precision) squared" + (let* ((maceps-computation-double (compute-maceps (lambda (x) x) + 1.0d0 + 1.0d0)) + (maceps-computation-single (compute-maceps (lambda (x) x) + 1.0 + 1.0)) + (last-double-h (cadar (last maceps-computation-double))) + (last-single-h (cadar (last maceps-computation-single)))) + (is (within-range-p + (- last-double-h (* last-single-h last-single-h)) + 0 + last-single-h)))) diff --git a/Homework/math4610/deprecated-cl/tests,suite.lisp b/Homework/math4610/deprecated-cl/tests,suite.lisp new file mode 100644 index 0000000..e23cfaf --- /dev/null +++ b/Homework/math4610/deprecated-cl/tests,suite.lisp @@ -0,0 +1,10 @@ +(in-package :cl-user) +(defpackage lizfcm/tests + (:use :cl + :fiveam) + (:export :run! + :lizfcm-test-suite)) +(in-package :lizfcm/tests) + +(def-suite lizfcm-test-suite + :description "The ultimate parent test suite") diff --git a/Homework/math4610/deprecated-cl/tests,table.lisp b/Homework/math4610/deprecated-cl/tests,table.lisp new file mode 100644 index 0000000..33d4e86 --- /dev/null +++ b/Homework/math4610/deprecated-cl/tests,table.lisp @@ -0,0 +1,31 @@ +(defpackage lizfcm/tests.table + (:use :cl + :fiveam + :lizfcm.utils + :lizfcm/tests) + (:export :approx-suite)) +(in-package :lizfcm/tests.table) + +(def-suite table-suite + :in lizfcm-test-suite) +(in-suite table-suite) + +(defun fib (n) + (cond ((< n 2) n) + (t (+ (fib (- n 1)) (fib (- n 2)))))) + +(test table-of-fib-vals + :description "table generates correctly" + (let* ((headers '("n" "fib(n)")) + (n-values '((1) (2) (3) (4))) + (expected `(("n" "fib(n)") + (1 ,(fib 1)) + (2 ,(fib 2)) + (3 ,(fib 3)) + (4 ,(fib 4)))) + (tabled (lizfcm.utils:table (:headers headers + :domain-order (n) + :domain-values n-values) + (fib n)))) + (is (equal expected tabled)))) + diff --git a/Homework/math4610/deprecated-cl/tests,vector.lisp b/Homework/math4610/deprecated-cl/tests,vector.lisp new file mode 100644 index 0000000..6edb1ac --- /dev/null +++ b/Homework/math4610/deprecated-cl/tests,vector.lisp @@ -0,0 +1,42 @@ +(defpackage lizfcm/tests.vector + (:use :cl + :fiveam + :lizfcm.vector + :lizfcm.utils + :lizfcm/tests) + (:export :vector-suite)) +(in-package :lizfcm/tests.vector) + +(def-suite vector-suite + :in lizfcm-test-suite) +(in-suite vector-suite) + +(test p-norm + :description "computes p-norm" + (let ((v '(1 1)) + (length (sqrt 2)) + (2-norm (p-norm 2))) + (is (within-range-p (funcall 2-norm v) + length + 0.00001)))) + +(test vector-distance + :description "computes distance via norm" + (let ((v1 '(0 0)) + (v2 '(1 1)) + (dist (sqrt 2))) + (is (within-range-p (distance v1 v2 (p-norm 2)) + dist + 0.00001)))) + +(test least-squares + :description "least squares is correct enough" + (let ((x '(0 1 2 3 4)) + (y '(1 2 3 4 5))) + (destructuring-bind (m b) (lizfcm.vector:least-squares-reg x y) + (is (within-range-p m 1 0.00001)) + (is (within-range-p b 1 0.00001)))) + (let ((x '(1 2 3 4 5 6 7)) + (y '(0.5 3 2 3.5 5 6 7.5))) + (destructuring-bind (m b) (lizfcm.vector:least-squares-reg x y) + (is (within-range-p m 1 0.3))))) ;; just a guestimate for best fit diff --git a/Homework/math4610/deprecated-cl/utils,package.lisp b/Homework/math4610/deprecated-cl/utils,package.lisp new file mode 100644 index 0000000..bdd5589 --- /dev/null +++ b/Homework/math4610/deprecated-cl/utils,package.lisp @@ -0,0 +1,5 @@ +(in-package :cl-user) +(defpackage lizfcm.utils + (:use :cl) + (:export :within-range-p + :table)) diff --git a/Homework/math4610/deprecated-cl/utils,table.lisp b/Homework/math4610/deprecated-cl/utils,table.lisp new file mode 100644 index 0000000..e96f37b --- /dev/null +++ b/Homework/math4610/deprecated-cl/utils,table.lisp @@ -0,0 +1,11 @@ +(in-package :lizfcm.utils) + +(defmacro table ((&key headers domain-order domain-values) &body body) + `(cons + ,headers + (mapcar (lambda (tuple) + (destructuring-bind ,domain-order tuple + (append tuple + (list + ,@body)))) + ,domain-values))) diff --git a/Homework/math4610/deprecated-cl/utils,within-range.lisp b/Homework/math4610/deprecated-cl/utils,within-range.lisp new file mode 100644 index 0000000..9a0b762 --- /dev/null +++ b/Homework/math4610/deprecated-cl/utils,within-range.lisp @@ -0,0 +1,5 @@ +(in-package :lizfcm.utils) + +(defun within-range-p (x true-value delta) + (and (< x (+ true-value delta)) + (> x (- true-value delta)))) diff --git a/Homework/math4610/deprecated-cl/vector,distance.lisp b/Homework/math4610/deprecated-cl/vector,distance.lisp new file mode 100644 index 0000000..74631ce --- /dev/null +++ b/Homework/math4610/deprecated-cl/vector,distance.lisp @@ -0,0 +1,6 @@ +(in-package :lizfcm.vector) + +(defun distance (v1 v2 norm) + (let* ((d (mapcar #'- v1 v2)) + (length (funcall norm d))) + length)) diff --git a/Homework/math4610/deprecated-cl/vector,least-squares.lisp b/Homework/math4610/deprecated-cl/vector,least-squares.lisp new file mode 100644 index 0000000..687af32 --- /dev/null +++ b/Homework/math4610/deprecated-cl/vector,least-squares.lisp @@ -0,0 +1,14 @@ +(in-package :lizfcm.vector) + +(defun least-squares-reg (x y) + (let* ((n (length x)) + (sum-y (reduce #'+ y)) + (sum-x (reduce #'+ x)) + (sum-xy (reduce #'+ (mapcar #'* x y))) + (sum-xsquared (reduce #'+ (mapcar #'* x x))) + (b (/ (- (* sum-y sum-xsquared) (* sum-x sum-xy)) + (- (* n sum-xsquared) (* sum-x sum-x)))) + (a (/ (- (* n sum-xy) (* sum-x sum-y)) + (- (* n sum-xsquared) (* sum-x sum-x))))) + (list a b))) + diff --git a/Homework/math4610/deprecated-cl/vector,norm.lisp b/Homework/math4610/deprecated-cl/vector,norm.lisp new file mode 100644 index 0000000..aa51bce --- /dev/null +++ b/Homework/math4610/deprecated-cl/vector,norm.lisp @@ -0,0 +1,14 @@ +(in-package :lizfcm.vector) + +(defun p-norm (p) + (lambda (v) + (expt + (reduce #'+ + (mapcar (lambda (x) + (abs + (expt x p))) + v)) + (/ 1 p)))) + +(defun max-norm (v) + (reduce #'max v)) diff --git a/Homework/math4610/deprecated-cl/vector,package.lisp b/Homework/math4610/deprecated-cl/vector,package.lisp new file mode 100644 index 0000000..f491908 --- /dev/null +++ b/Homework/math4610/deprecated-cl/vector,package.lisp @@ -0,0 +1,8 @@ +(in-package :cl-user) +(defpackage lizfcm.vector + (:use :cl) + (:export + :p-norm + :max-norm + :distance + :least-squares-reg)) diff --git a/Homework/math4610/dist/lizfcm.test b/Homework/math4610/dist/lizfcm.test Binary files differnew file mode 100644 index 0000000..8e31a6c --- /dev/null +++ b/Homework/math4610/dist/lizfcm.test diff --git a/Homework/math4610/doc/software_manual.org b/Homework/math4610/doc/software_manual.org new file mode 100644 index 0000000..b41ec03 --- /dev/null +++ b/Homework/math4610/doc/software_manual.org @@ -0,0 +1,1376 @@ +#+TITLE: LIZFCM Software Manual (v0.6) +#+AUTHOR: Elizabeth Hunt +#+LATEX_HEADER: \notindent \notag \usepackage{amsmath} \usepackage[a4paper,margin=1in,portrait]{geometry} +#+LATEX: \setlength\parindent{0pt} +#+STARTUP: entitiespretty fold inlineimages + +* Design +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 +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: + ++ 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 "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 ~arm~-based M1 machine running +MacOS as well as ~x86~ Arch Linux. + +1. ~cd~ into the root of the repo +2. ~make~ + +Then, as of homework 5, the testing routines are provided in ~test~ and utilize the +~utest~ "micro"library. They compile to a binary in ~./dist/lizfcm.test~. + +Execution of the Makefile will perform compilation of individual routines. + +But, in the requirement of manual intervention (should the little alien workers +inside the computer fail to do their job), one can use the following command to +produce an object file: + +\begin{verbatim} + gcc -Iinc/ -lm -Wall -c src/<the_routine>.c -o build/<the_routine>.o +\end{verbatim} + +Which is then bundled into a static library in ~lib/lizfcm.a~ and can be linked +in the standard method. + +* The LIZFCM API +** Simple Routines +*** ~smaceps~ ++ Author: Elizabeth Hunt ++ Name: ~smaceps~ ++ Location: ~src/maceps.c~ ++ Input: none ++ Output: a ~float~ returning the specific "Machine Epsilon" of a machine on a + single precision floating point number at which it becomes "indistinguishable". + +#+BEGIN_SRC c +float smaceps() { + float one = 1.0; + float machine_epsilon = 1.0; + float one_approx = one + machine_epsilon; + + while (fabsf(one_approx - one) > 0) { + machine_epsilon /= 2; + one_approx = one + machine_epsilon; + } + + return machine_epsilon; +} +#+END_SRC + +*** ~dmaceps~ ++ Author: Elizabeth Hunt ++ Name: ~dmaceps~ ++ Location: ~src/maceps.c~ ++ Input: none ++ Output: a ~double~ returning the specific "Machine Epsilon" of a machine on a + double precision floating point number at which it becomes "indistinguishable". + +#+BEGIN_SRC c +double dmaceps() { + double one = 1.0; + double machine_epsilon = 1.0; + double one_approx = one + machine_epsilon; + + while (fabs(one_approx - one) > 0) { + machine_epsilon /= 2; + one_approx = one + machine_epsilon; + } + + return machine_epsilon; +} +#+END_SRC + +** Derivative Routines +*** ~central_derivative_at~ ++ Author: Elizabeth Hunt ++ Name: ~central_derivative_at~ ++ Location: ~src/approx_derivative.c~ ++ Input: + - ~f~ is a pointer to a one-ary function that takes a double as input and produces + a double as output + - ~a~ is the domain value at which we approximate ~f'~ + - ~h~ is the step size ++ Output: a ~double~ of the approximate value of ~f'(a)~ via the central difference + method. + +#+BEGIN_SRC c +double central_derivative_at(double (*f)(double), double a, double h) { + assert(h > 0); + + double x2 = a + h; + double x1 = a - h; + + double y2 = f(x2); + double y1 = f(x1); + + return (y2 - y1) / (x2 - x1); +} +#+END_SRC + +*** ~forward_derivative_at~ ++ Author: Elizabeth Hunt ++ Name: ~forward_derivative_at~ ++ Location: ~src/approx_derivative.c~ ++ Input: + - ~f~ is a pointer to a one-ary function that takes a double as input and produces + a double as output + - ~a~ is the domain value at which we approximate ~f'~ + - ~h~ is the step size ++ Output: a ~double~ of the approximate value of ~f'(a)~ via the forward difference + method. + +#+BEGIN_SRC c +double forward_derivative_at(double (*f)(double), double a, double h) { + assert(h > 0); + + double x2 = a + h; + double x1 = a; + + double y2 = f(x2); + double y1 = f(x1); + + return (y2 - y1) / (x2 - x1); +} +#+END_SRC + +*** ~backward_derivative_at~ ++ Author: Elizabeth Hunt ++ Name: ~backward_derivative_at~ ++ Location: ~src/approx_derivative.c~ ++ Input: + - ~f~ is a pointer to a one-ary function that takes a double as input and produces + a double as output + - ~a~ is the domain value at which we approximate ~f'~ + - ~h~ is the step size ++ Output: a ~double~ of the approximate value of ~f'(a)~ via the backward difference + method. + +#+BEGIN_SRC c +double backward_derivative_at(double (*f)(double), double a, double h) { + assert(h > 0); + + double x2 = a; + double x1 = a - h; + + double y2 = f(x2); + double y1 = f(x1); + + return (y2 - y1) / (x2 - x1); +} +#+END_SRC + +** Vector Routines +*** Vector Arithmetic: ~add_v, minus_v~ ++ Author: Elizabeth Hunt ++ Name(s): ~add_v~, ~minus_v~ ++ Location: ~src/vector.c~ ++ Input: two pointers to locations in memory wherein ~Array_double~'s lie ++ Output: a pointer to a new ~Array_double~ as the result of addition or subtraction + of the two input ~Array_double~'s + +#+BEGIN_SRC c +Array_double *add_v(Array_double *v1, Array_double *v2) { + assert(v1->size == v2->size); + + Array_double *sum = copy_vector(v1); + for (size_t i = 0; i < v1->size; i++) + sum->data[i] += v2->data[i]; + return sum; +} + +Array_double *minus_v(Array_double *v1, Array_double *v2) { + assert(v1->size == v2->size); + + Array_double *sub = InitArrayWithSize(double, v1->size, 0); + for (size_t i = 0; i < v1->size; i++) + sub->data[i] = v1->data[i] - v2->data[i]; + return sub; +} +#+END_SRC + +*** Norms: ~l1_norm~, ~l2_norm~, ~linf_norm~ ++ Author: Elizabeth Hunt ++ Name(s): ~l1_norm~, ~l2_norm~, ~linf_norm~ ++ Location: ~src/vector.c~ ++ Input: a pointer to a location in memory wherein an ~Array_double~ lies ++ Output: a ~double~ representing the value of the norm the function applies + +#+BEGIN_SRC c +double l1_norm(Array_double *v) { + double sum = 0; + for (size_t i = 0; i < v->size; ++i) + sum += fabs(v->data[i]); + return sum; +} + +double l2_norm(Array_double *v) { + double norm = 0; + for (size_t i = 0; i < v->size; ++i) + norm += v->data[i] * v->data[i]; + return sqrt(norm); +} + +double linf_norm(Array_double *v) { + assert(v->size > 0); + double max = v->data[0]; + for (size_t i = 0; i < v->size; ++i) + max = c_max(v->data[i], max); + return max; +} +#+END_SRC + +*** ~vector_distance~ ++ Author: Elizabeth Hunt ++ Name: ~vector_distance~ ++ Location: ~src/vector.c~ ++ Input: two pointers to locations in memory wherein ~Array_double~'s lie, and a pointer to a + one-ary function ~norm~ taking as input a pointer to an ~Array_double~ and returning a double + representing the norm of that ~Array_double~ + +#+BEGIN_SRC c +double vector_distance(Array_double *v1, Array_double *v2, + double (*norm)(Array_double *)) { + Array_double *minus = minus_v(v1, v2); + double dist = (*norm)(minus); + free(minus); + return dist; +} +#+END_SRC + +*** Distances: ~l1_distance~, ~l2_distance~, ~linf_distance~ ++ Author: Elizabeth Hunt ++ Name(s): ~l1_distance~, ~l2_distance~, ~linf_distance~ ++ Location: ~src/vector.c~ ++ Input: two pointers to locations in memory wherein ~Array_double~'s lie, and the distance + via the corresponding ~l1~, ~l2~, or ~linf~ norms ++ Output: A ~double~ representing the distance between the two ~Array_doubles~'s by the given + norm. + +#+BEGIN_SRC c +double l1_distance(Array_double *v1, Array_double *v2) { + return vector_distance(v1, v2, &l1_norm); +} + +double l2_distance(Array_double *v1, Array_double *v2) { + return vector_distance(v1, v2, &l2_norm); +} + +double linf_distance(Array_double *v1, Array_double *v2) { + return vector_distance(v1, v2, &linf_norm); +} +#+END_SRC + +*** ~sum_v~ ++ Author: Elizabeth Hunt ++ Name: ~sum_v~ ++ Location: ~src/vector.c~ ++ Input: a pointer to an ~Array_double~ ++ Output: a ~double~ representing the sum of all the elements of an ~Array_double~ + +#+BEGIN_SRC c +double sum_v(Array_double *v) { + double sum = 0; + for (size_t i = 0; i < v->size; i++) + sum += v->data[i]; + return sum; +} +#+END_SRC + +*** ~scale_v~ ++ Author: Elizabeth Hunt ++ Name: ~scale_v~ ++ Location: ~src/vector.c~ ++ Input: a pointer to an ~Array_double~ and a scalar ~double~ to scale the vector ++ Output: a pointer to a new ~Array_double~ of the scaled input ~Array_double~ + +#+BEGIN_SRC c +Array_double *scale_v(Array_double *v, double m) { + Array_double *copy = copy_vector(v); + for (size_t i = 0; i < v->size; i++) + copy->data[i] *= m; + return copy; +} +#+END_SRC + +*** ~free_vector~ ++ Author: Elizabeth Hunt ++ Name: ~free_vector~ ++ Location: ~src/vector.c~ ++ Input: a pointer to an ~Array_double~ ++ Output: nothing. ++ Side effect: free the memory of the reserved ~Array_double~ on the heap + +#+BEGIN_SRC c +void free_vector(Array_double *v) { + free(v->data); + free(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~ ++ Location: ~src/vector.c~ ++ Input: a pointer to an ~Array_double~ ++ Output: a pointer to a new ~Array_double~ whose ~data~ and ~size~ are copied from the input + ~Array_double~ + +#+BEGIN_SRC c +Array_double *copy_vector(Array_double *v) { + Array_double *copy = InitArrayWithSize(double, v->size, 0.0); + for (size_t i = 0; i < copy->size; ++i) + copy->data[i] = v->data[i]; + return copy; +} +#+END_SRC + +*** ~format_vector_into~ ++ Author: Elizabeth Hunt ++ Name: ~format_vector_into~ ++ Location: ~src/vector.c~ ++ Input: a pointer to an ~Array_double~ and a pointer to a c-string ~s~ to "print" the vector out + into ++ Output: nothing. ++ Side effect: overwritten memory into ~s~ + +#+BEGIN_SRC c +void format_vector_into(Array_double *v, char *s) { + if (v->size == 0) { + strcat(s, "empty"); + return; + } + + for (size_t i = 0; i < v->size; ++i) { + char num[64]; + strcpy(num, ""); + + sprintf(num, "%f,", v->data[i]); + strcat(s, num); + } + strcat(s, "\n"); +} +#+END_SRC + +** Matrix Routines +*** ~lu_decomp~ ++ Author: Elizabeth Hunt ++ Name: ~lu_decomp~ ++ Location: ~src/matrix.c~ ++ Input: a pointer to a ~Matrix_double~ $m$ to decompose into a lower triangular and upper triangular + 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: 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 *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); + + 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 (denom == 0) { + printf("ERROR: non-factorable matrix\n"); + assert(false); + } + + 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]); + + u->data[y] = added; + l->data[y]->data[x] = -factor; + } + } + } + + u_l[0] = u; + u_l[1] = l; + return u_l; +} +#+END_SRC +*** ~bsubst~ ++ Author: Elizabeth Hunt ++ Name: ~bsubst~ ++ Location: ~src/matrix.c~ ++ Input: a pointer to an upper-triangular ~Matrix_double~ $u$ and a ~Array_double~ + $b$ ++ Output: a pointer to a new ~Array_double~ whose entries are given by performing + back substitution + +#+BEGIN_SRC c +Array_double *bsubst(Matrix_double *u, Array_double *b) { + assert(u->rows == b->size && u->cols == u->rows); + + Array_double *x = copy_vector(b); + for (int64_t row = b->size - 1; row >= 0; row--) { + for (size_t col = b->size - 1; col > row; col--) + x->data[row] -= x->data[col] * u->data[row]->data[col]; + x->data[row] /= u->data[row]->data[row]; + } + return x; +} +#+END_SRC +*** ~fsubst~ ++ Author: Elizabeth Hunt ++ Name: ~fsubst~ ++ Location: ~src/matrix.c~ ++ Input: a pointer to a lower-triangular ~Matrix_double~ $l$ and a ~Array_double~ + $b$ ++ Output: a pointer to a new ~Array_double~ whose entries are given by performing + forward substitution + +#+BEGIN_SRC c +Array_double *fsubst(Matrix_double *l, Array_double *b) { + assert(l->rows == b->size && l->cols == l->rows); + + Array_double *x = copy_vector(b); + + for (size_t row = 0; row < b->size; row++) { + for (size_t col = 0; col < row; col++) + x->data[row] -= x->data[col] * l->data[row]->data[col]; + x->data[row] /= l->data[row]->data[row]; + } + + return x; +} +#+END_SRC + +*** ~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$ ++ Output: $x$ such that $mx = b$ if such a solution exists (else it's non LU-factorable as discussed + above) + +Here we make use of forward substitution to first solve $Ly = b$ given $L$ as the $L$ factor in +~lu_decomp~. Then we use back substitution to solve $Ux = y$ for $x$ similarly given $U$. + +Then, $LUx = b$, thus $x$ is a solution. + +#+BEGIN_SRC c +Array_double *solve_matrix_lu_bsubst(Matrix_double *m, Array_double *b) { + assert(b->size == m->rows); + assert(m->rows == m->cols); + + Array_double *x = copy_vector(b); + Matrix_double **u_l = lu_decomp(m); + Matrix_double *u = u_l[0]; + Matrix_double *l = u_l[1]; + + Array_double *b_fsub = fsubst(l, b); + x = bsubst(u, b_fsub); + free_vector(b_fsub); + + 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, k = 0; + + Matrix_double *m_cp = copy_matrix(m); + + while (h < m_cp->rows && k < m_cp->cols) { + uint64_t max_row = h; + double max_val = 0.0; + + for (uint64_t row = h; row < m_cp->rows; row++) { + double val = fabs(m_cp->data[row]->data[k]); + if (val > max_val) { + max_val = val; + max_row = row; + } + } + + if (max_val == 0.0) { + k++; + continue; + } + + if (max_row != h) { + 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~ ++ Input: a pointer to a ~Matrix_double~ $m$ and ~Array_double~ $v$ ++ Output: the dot product $mv$ as an ~Array_double~ + +#+BEGIN_SRC c +Array_double *m_dot_v(Matrix_double *m, Array_double *v) { + assert(v->size == m->cols); + + Array_double *product = copy_vector(v); + + for (size_t row = 0; row < v->size; ++row) + product->data[row] = v_dot_v(m->data[row], v); + + return product; +} +#+END_SRC + +*** ~put_identity_diagonal~ ++ Author: Elizabeth Hunt ++ Location: ~src/matrix.c~ ++ Input: a pointer to a ~Matrix_double~ ++ Output: a pointer to a copy to ~Matrix_double~ whose diagonal is full of 1's + +#+BEGIN_SRC c +Matrix_double *put_identity_diagonal(Matrix_double *m) { + assert(m->rows == m->cols); + Matrix_double *copy = copy_matrix(m); + for (size_t y = 0; y < m->rows; ++y) + copy->data[y]->data[y] = 1.0; + return copy; +} +#+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~ ++ Input: a pointer to a ~Matrix_double~ ++ Output: a pointer to a copy of the given ~Matrix_double~ + +#+BEGIN_SRC c +Matrix_double *copy_matrix(Matrix_double *m) { + Matrix_double *copy = InitMatrixWithSize(double, m->rows, m->cols, 0.0); + for (size_t y = 0; y < copy->rows; y++) { + free_vector(copy->data[y]); + copy->data[y] = copy_vector(m->data[y]); + } + return copy; +} +#+END_SRC + +*** ~free_matrix~ ++ Author: Elizabeth Hunt ++ Location: ~src/matrix.c~ ++ Input: a pointer to a ~Matrix_double~ ++ Output: none. ++ Side Effects: frees memory reserved by a given ~Matrix_double~ and its member + ~Array_double~ vectors describing its rows. + +#+BEGIN_SRC c +void free_matrix(Matrix_double *m) { + for (size_t y = 0; y < m->rows; ++y) + free_vector(m->data[y]); + free(m); +} +#+END_SRC + +*** ~format_matrix_into~ ++ Author: Elizabeth Hunt ++ Name: ~format_matrix_into~ ++ Location: ~src/matrix.c~ ++ Input: a pointer to a ~Matrix_double~ and a pointer to a c-string ~s~ to "print" the vector out + into ++ Output: nothing. ++ Side effect: overwritten memory into ~s~ + +#+BEGIN_SRC c +void format_matrix_into(Matrix_double *m, char *s) { + if (m->rows == 0) + strcpy(s, "empty"); + + for (size_t y = 0; y < m->rows; ++y) { + char row_s[5192]; + strcpy(row_s, ""); + + format_vector_into(m->data[y], row_s); + strcat(s, row_s); + } + strcat(s, "\n"); +} +#+END_SRC +** Root Finding Methods +*** ~find_ivt_range~ ++ Author: Elizabeth Hunt ++ Name: ~find_ivt_range~ ++ Location: ~src/roots.c~ ++ Input: a pointer to a oneary function taking a double and producing a double, the beginning point + in $R$ to search for a range, a ~delta~ step that is taken, and a ~max_steps~ number of maximum + iterations to perform. ++ Output: a pair of ~double~'s in an ~Array_double~ representing a closed closed interval ~[beginning, end]~ + +#+BEGIN_SRC c +// f is well defined at start_x + delta*n for all n on the integer range [0, +// max_iterations] +Array_double *find_ivt_range(double (*f)(double), double start_x, double delta, + size_t max_iterations) { + double a = start_x; + + while (f(a) * f(a + delta) >= 0 && max_iterations > 0) { + max_iterations--; + a += delta; + } + + double end = a + delta; + double begin = a - delta; + + if (max_iterations == 0 && f(begin) * f(end) >= 0) + return NULL; + return InitArray(double, {begin, end}); +} +#+END_SRC +*** ~bisect_find_root~ ++ Author: Elizabeth Hunt ++ Name(s): ~bisect_find_root~ ++ Input: a one-ary function taking a double and producing a double, a closed interval represented + by ~a~ and ~b~: ~[a, b]~, a ~tolerance~ at which we return the estimated root once $b-a < \text{tolerance}$, and a + ~max_iterations~ to break us out of a loop if we can never reach the ~tolerance~. ++ Output: a vector of size of 3, ~double~'s representing first the range ~[a,b]~ and then the midpoint, + ~c~ of the range. ++ Description: recursively uses binary search to split the interval until we reach ~tolerance~. We + also assume the function ~f~ is continuous on ~[a, b]~. + +#+BEGIN_SRC c +// f is continuous on [a, b] +Array_double *bisect_find_root(double (*f)(double), double a, double b, + double tolerance, size_t max_iterations) { + assert(a <= b); + // guarantee there's a root somewhere between a and b by IVT + assert(f(a) * f(b) < 0); + + double c = (1.0 / 2) * (a + b); + if (b - a < tolerance || max_iterations == 0) + return InitArray(double, {a, b, c}); + + if (f(a) * f(c) < 0) + return bisect_find_root(f, a, c, tolerance, max_iterations - 1); + return bisect_find_root(f, c, b, tolerance, max_iterations - 1); +} +#+END_SRC +*** ~bisect_find_root_with_error_assumption~ ++ Author: Elizabeth Hunt ++ Name: ~bisect_find_root_with_error_assumption~ ++ Input: a one-ary function taking a double and producing a double, a closed interval represented + by ~a~ and ~b~: ~[a, b]~, and a ~tolerance~ equivalent to the above definition in ~bisect_find_root~ ++ Output: a ~double~ representing the estimated root ++ Description: using the bisection method we know that $e_k \le (\frac{1}{2})^k (b_0 - a_0)$. So we can + calculate $k$ at the worst possible case (that the error is exactly the tolerance) to be + $\frac{log(tolerance) - log(b_0 - a_0)}{log(\frac{1}{2})}$. We pass this value into the ~max_iterations~ + of ~bisect_find_root~ as above. +#+BEGIN_SRC c +double bisect_find_root_with_error_assumption(double (*f)(double), double a, + double b, double tolerance) { + assert(a <= b); + + uint64_t max_iterations = + (uint64_t)ceil((log(tolerance) - log(b - a)) / log(1 / 2.0)); + + Array_double *a_b_root = bisect_find_root(f, a, b, tolerance, max_iterations); + double root = a_b_root->data[2]; + free_vector(a_b_root); + + return root; +} +#+END_SRC + +*** ~fixed_point_iteration_method~ ++ Author: Elizabeth Hunt ++ Name: ~fixed_point_iteration_method~ ++ Location: ~src/roots.c~ ++ Input: a pointer to a oneary function $f$ taking a double and producing a double of which we are + trying to find a root, a guess $x_0$, and a function $g$ of the same signature of $f$ at which we + "step" our guesses according to the fixed point iteration method: $x_k = g(x_{k-1})$. Additionally, a + ~max_iterations~ representing the maximum number of "steps" to take before arriving at our + approximation and a ~tolerance~ to return our root if it becomes within [0 - tolerance, 0 + tolerance]. ++ Assumptions: $g(x)$ must be a function such that at the point $x^*$ (the found root) the derivative + $|g'(x^*)| \lt 1$ ++ Output: a double representing the found approximate root $\approx x^*$. + +#+BEGIN_SRC c +double fixed_point_iteration_method(double (*f)(double), double (*g)(double), + double x_0, double tolerance, + size_t max_iterations) { + if (max_iterations <= 0) + return x_0; + + double root = g(x_0); + if (tolerance >= fabs(f(root))) + return root; + + return fixed_point_iteration_method(f, g, root, tolerance, + max_iterations - 1); +} +#+END_SRC + +*** ~fixed_point_newton_method~ ++ Author: Elizabeth Hunt ++ Name: ~fixed_point_newton_method~ ++ Location: ~src/roots.c~ ++ Input: a pointer to a oneary function $f$ taking a double and producing a double of which we are + trying to find a root and another pointer to a function fprime of the same signature, a guess $x_0$, + and a ~max_iterations~ and ~tolerance~ as defined in the above method are required inputs. ++ Description: continually computes elements in the sequence $x_n = x_{n-1} - \frac{f(x_{n-1})}{f'p(x_{n-1})}$ ++ Output: a double representing the found approximate root $\approx x^*$ recursively applied to the sequence + given +#+BEGIN_SRC c +double fixed_point_newton_method(double (*f)(double), double (*fprime)(double), + double x_0, double tolerance, + size_t max_iterations) { + if (max_iterations <= 0) + return x_0; + + double root = x_0 - f(x_0) / fprime(x_0); + if (tolerance >= fabs(f(root))) + return root; + + return fixed_point_newton_method(f, fprime, root, tolerance, + max_iterations - 1); +} +#+END_SRC + +*** ~fixed_point_secant_method~ ++ Author: Elizabeth Hunt ++ Name: ~fixed_point_secant_method~ ++ Location: ~src/roots.c~ ++ Input: a pointer to a oneary function $f$ taking a double and producing a double of which we are + trying to find a root, a guess $x_0$ and $x_1$ in which a root lies between $[x_0, x_1]$; applying the + sequence $x_n = x_{n-1} - f(x_{n-1}) \frac{x_{n-1} - x_{n-2}}{f(x_{n-1}) - f(x_{n-2})}$. + Additionally, a ~max_iterations~ and ~tolerance~ as defined in the above method are required + inputs. ++ Output: a double representing the found approximate root $\approx x^*$ recursively applied to the sequence. +#+BEGIN_SRC c +double fixed_point_secant_method(double (*f)(double), double x_0, double x_1, + double tolerance, size_t max_iterations) { + if (max_iterations == 0) + return x_1; + + double root = x_1 - f(x_1) * ((x_1 - x_0) / (f(x_1) - f(x_0))); + + if (tolerance >= fabs(f(root))) + return root; + + return fixed_point_secant_method(f, x_1, root, tolerance, max_iterations - 1); +} +#+END_SRC +*** ~fixed_point_secant_bisection_method~ ++ Author: Elizabeth Hunt ++ Name: ~fixed_point_secant_method~ ++ Location: ~src/roots.c~ ++ Input: a pointer to a oneary function $f$ taking a double and producing a double of which we are + trying to find a root, a guess $x_0$, and a $x_1$ of which we define our first interval $[x_0, x_1]$. + Then, we perform a single iteration of the ~fixed_point_secant_method~ on this interval; if it + produces a root outside, we refresh the interval and root respectively with the given + ~bisect_find_root~ method. Additionally, a ~max_iterations~ and ~tolerance~ as defined in the above method are required + inputs. ++ Output: a double representing the found approximate root $\approx x^*$ continually applied with the + constraints defined. + +#+BEGIN_SRC c +double fixed_point_secant_bisection_method(double (*f)(double), double x_0, + double x_1, double tolerance, + size_t max_iterations) { + double begin = x_0; + double end = x_1; + double root = x_0; + + while (tolerance < fabs(f(root)) && max_iterations > 0) { + max_iterations--; + + double secant_root = fixed_point_secant_method(f, begin, end, tolerance, 1); + + if (secant_root < begin || secant_root > end) { + Array_double *range_root = bisect_find_root(f, begin, end, tolerance, 1); + + begin = range_root->data[0]; + end = range_root->data[1]; + root = range_root->data[2]; + + free_vector(range_root); + continue; + } + + root = secant_root; + + if (f(root) * f(begin) < 0) + end = secant_root; // the root exists in [begin, secant_root] + else + begin = secant_root; + } + + return root; +} +#+END_SRC + +** Linear Routines +*** ~least_squares_lin_reg~ ++ Author: Elizabeth Hunt ++ Name: ~least_squares_lin_reg~ ++ Location: ~src/lin.c~ ++ Input: two pointers to ~Array_double~'s whose entries correspond two ordered + pairs in R^2 ++ Output: a linear model best representing the ordered pairs via least squares + regression + +#+BEGIN_SRC c +Line *least_squares_lin_reg(Array_double *x, Array_double *y) { + assert(x->size == y->size); + + uint64_t n = x->size; + double sum_x = sum_v(x); + double sum_y = sum_v(y); + double sum_xy = v_dot_v(x, y); + double sum_xx = v_dot_v(x, x); + double denom = ((n * sum_xx) - (sum_x * sum_x)); + + Line *line = malloc(sizeof(Line)); + line->m = ((sum_xy * n) - (sum_x * sum_y)) / denom; + line->a = ((sum_y * sum_xx) - (sum_x * sum_xy)) / denom; + + return line; +} +#+END_SRC + +** Eigen-Adjacent +*** ~dominant_eigenvalue~ ++ Author: Elizabeth Hunt ++ Name: ~dominant_eigenvalue~ ++ Location: ~src/eigen.c~ ++ Input: a pointer to an invertible matrix ~m~, an initial eigenvector guess ~v~ (that is non + zero or orthogonal to an eigenvector with the dominant eigenvalue), a ~tolerance~ and + ~max_iterations~ that act as stop conditions ++ Output: the dominant eigenvalue with the highest magnitude, approximated with the Power + Iteration Method + +#+BEGIN_SRC c +double dominant_eigenvalue(Matrix_double *m, Array_double *v, double tolerance, + size_t max_iterations) { + assert(m->rows == m->cols); + assert(m->rows == v->size); + + double error = tolerance; + size_t iter = max_iterations; + double lambda = 0.0; + Array_double *eigenvector_1 = copy_vector(v); + + while (error >= tolerance && (--iter) > 0) { + Array_double *eigenvector_2 = m_dot_v(m, eigenvector_1); + Array_double *normalized_eigenvector_2 = + scale_v(eigenvector_2, 1.0 / linf_norm(eigenvector_2)); + free_vector(eigenvector_2); + eigenvector_2 = normalized_eigenvector_2; + + Array_double *mx = m_dot_v(m, eigenvector_2); + double new_lambda = + v_dot_v(mx, eigenvector_2) / v_dot_v(eigenvector_2, eigenvector_2); + + error = fabs(new_lambda - lambda); + lambda = new_lambda; + free_vector(eigenvector_1); + eigenvector_1 = eigenvector_2; + } + + return lambda; +} +#+END_SRC +*** ~shift_inverse_power_eigenvalue~ ++ Author: Elizabeth Hunt ++ Name: ~least_dominant_eigenvalue~ ++ Location: ~src/eigen.c~ ++ Input: a pointer to an invertible matrix ~m~, an initial eigenvector guess ~v~ (that is non + zero or orthogonal to an eigenvector with the dominant eigenvalue), a ~shift~ to act as the + shifted \delta, and ~tolerance~ and ~max_iterations~ that act as stop conditions. ++ Output: the eigenvalue closest to ~shift~ with the lowest magnitude closest to 0, approximated + with the Inverse Power Iteration Method +#+BEGIN_SRC c +double shift_inverse_power_eigenvalue(Matrix_double *m, Array_double *v, + double shift, double tolerance, + size_t max_iterations) { + assert(m->rows == m->cols); + assert(m->rows == v->size); + + Matrix_double *m_c = copy_matrix(m); + for (size_t y = 0; y < m_c->rows; ++y) + m_c->data[y]->data[y] = m_c->data[y]->data[y] - shift; + + double error = tolerance; + size_t iter = max_iterations; + double lambda = shift; + Array_double *eigenvector_1 = copy_vector(v); + + while (error >= tolerance && (--iter) > 0) { + Array_double *eigenvector_2 = solve_matrix_lu_bsubst(m_c, eigenvector_1); + Array_double *normalized_eigenvector_2 = + scale_v(eigenvector_2, 1.0 / linf_norm(eigenvector_2)); + free_vector(eigenvector_2); + + Array_double *mx = m_dot_v(m, normalized_eigenvector_2); + double new_lambda = + v_dot_v(mx, normalized_eigenvector_2) / + v_dot_v(normalized_eigenvector_2, normalized_eigenvector_2); + + error = fabs(new_lambda - lambda); + lambda = new_lambda; + free_vector(eigenvector_1); + eigenvector_1 = normalized_eigenvector_2; + } + + return lambda; +} +#+END_SRC + +*** ~least_dominant_eigenvalue~ ++ Author: Elizabeth Hunt ++ Name: ~least_dominant_eigenvalue~ ++ Location: ~src/eigen.c~ ++ Input: a pointer to an invertible matrix ~m~, an initial eigenvector guess ~v~ (that is non + zero or orthogonal to an eigenvector with the dominant eigenvalue), a ~tolerance~ and + ~max_iterations~ that act as stop conditions. ++ Output: the least dominant eigenvalue with the lowest magnitude closest to 0, approximated + with the Inverse Power Iteration Method. +#+BEGIN_SRC c +double least_dominant_eigenvalue(Matrix_double *m, Array_double *v, + double tolerance, size_t max_iterations) { + return shift_inverse_power_eigenvalue(m, v, 0.0, tolerance, max_iterations); +} +#+END_SRC +*** ~partition_find_eigenvalues~ ++ Author: Elizabeth Hunt ++ Name: ~partition_find_eigenvalues~ ++ Location: ~src/eigen.c~ ++ Input: a pointer to an invertible matrix ~m~, a matrix whose rows correspond to initial + eigenvector guesses at each "partition" which is computed from a uniform distribution + between the number of rows this "guess matrix" has and the distance between the least + dominant eigenvalue and the most dominant. Additionally, a ~max_iterations~ and a ~tolerance~ + that act as stop conditions. ++ Output: a vector of ~doubles~ corresponding to the "nearest" eigenvalue at the midpoint of + each partition, via the given guess of that partition. +#+BEGIN_SRC c +Array_double *partition_find_eigenvalues(Matrix_double *m, + Matrix_double *guesses, + double tolerance, + size_t max_iterations) { + assert(guesses->rows >= + 2); // we need at least, the most and least dominant eigenvalues + + double end = dominant_eigenvalue(m, guesses->data[guesses->rows - 1], + tolerance, max_iterations); + double begin = + least_dominant_eigenvalue(m, guesses->data[0], tolerance, max_iterations); + + double delta = (end - begin) / guesses->rows; + Array_double *eigenvalues = InitArrayWithSize(double, guesses->rows, 0.0); + for (size_t i = 0; i < guesses->rows; i++) { + double box_midpoint = ((delta * i) + (delta * (i + 1))) / 2; + + double nearest_eigenvalue = shift_inverse_power_eigenvalue( + m, guesses->data[i], box_midpoint, tolerance, max_iterations); + + eigenvalues->data[i] = nearest_eigenvalue; + } + + return eigenvalues; +} +#+END_SRC +*** ~leslie_matrix~ ++ Author: Elizabeth Hunt ++ Name: ~leslie_matrix~ ++ Location: ~src/eigen.c~ ++ Input: two pointers to ~Array_double~'s representing the ratio of individuals in an age class + $x$ getting to the next age class $x+1$ and the number of offspring that individuals in an age + class create in age class 0. ++ Output: the leslie matrix generated from the input vectors. + +#+BEGIN_SRC c +Matrix_double *leslie_matrix(Array_double *age_class_surivor_ratio, + Array_double *age_class_offspring) { + assert(age_class_surivor_ratio->size + 1 == age_class_offspring->size); + + Matrix_double *leslie = InitMatrixWithSize(double, age_class_offspring->size, + age_class_offspring->size, 0.0); + + free_vector(leslie->data[0]); + leslie->data[0] = age_class_offspring; + + for (size_t i = 0; i < age_class_surivor_ratio->size; i++) + leslie->data[i + 1]->data[i] = age_class_surivor_ratio->data[i]; + return leslie; +} +#+END_SRC +** Jacobi / Gauss-Siedel +*** ~jacobi_solve~ ++ Author: Elizabeth Hunt ++ Name: ~jacobi_solve~ ++ Location: ~src/matrix.c~ ++ Input: a pointer to a diagonally dominant square matrix $m$, a vector representing + the value $b$ in $mx = b$, a double representing the maximum distance between + the solutions produced by iteration $i$ and $i+1$ (by L2 norm a.k.a cartesian + distance), and a ~max_iterations~ which we force stop. ++ Output: the converged-upon solution $x$ to $mx = b$ +#+BEGIN_SRC c +Array_double *jacobi_solve(Matrix_double *m, Array_double *b, + double l2_convergence_tolerance, + size_t max_iterations) { + assert(m->rows == m->cols); + assert(b->size == m->cols); + size_t iter = max_iterations; + + Array_double *x_k = InitArrayWithSize(double, b->size, 0.0); + Array_double *x_k_1 = + InitArrayWithSize(double, b->size, rand_from(0.1, 10.0)); + + while ((--iter) > 0 && l2_distance(x_k_1, x_k) > l2_convergence_tolerance) { + for (size_t i = 0; i < m->rows; i++) { + double delta = 0.0; + for (size_t j = 0; j < m->cols; j++) { + if (i == j) + continue; + delta += m->data[i]->data[j] * x_k->data[j]; + } + x_k_1->data[i] = (b->data[i] - delta) / m->data[i]->data[i]; + } + + Array_double *tmp = x_k; + x_k = x_k_1; + x_k_1 = tmp; + } + + free_vector(x_k); + return x_k_1; +} +#+END_SRC + +*** ~gauss_siedel_solve~ ++ Author: Elizabeth Hunt ++ Name: ~gauss_siedel_solve~ ++ Location: ~src/matrix.c~ ++ Input: a pointer to a [[https://en.wikipedia.org/wiki/Gauss%E2%80%93Seidel_method][diagonally dominant or symmetric and positive definite]] + square matrix $m$, a vector representing + the value $b$ in $mx = b$, a double representing the maximum distance between + the solutions produced by iteration $i$ and $i+1$ (by L2 norm a.k.a cartesian + distance), and a ~max_iterations~ which we force stop. ++ Output: the converged-upon solution $x$ to $mx = b$ ++ Description: we use almost the exact same method as ~jacobi_solve~ but modify + only one array in accordance to the Gauss-Siedel method, but which is necessarily + copied before due to the convergence check. +#+BEGIN_SRC c + +Array_double *gauss_siedel_solve(Matrix_double *m, Array_double *b, + double l2_convergence_tolerance, + size_t max_iterations) { + assert(m->rows == m->cols); + assert(b->size == m->cols); + size_t iter = max_iterations; + + Array_double *x_k = InitArrayWithSize(double, b->size, 0.0); + Array_double *x_k_1 = + InitArrayWithSize(double, b->size, rand_from(0.1, 10.0)); + + while ((--iter) > 0) { + for (size_t i = 0; i < x_k->size; i++) + x_k->data[i] = x_k_1->data[i]; + + for (size_t i = 0; i < m->rows; i++) { + double delta = 0.0; + for (size_t j = 0; j < m->cols; j++) { + if (i == j) + continue; + delta += m->data[i]->data[j] * x_k_1->data[j]; + } + x_k_1->data[i] = (b->data[i] - delta) / m->data[i]->data[i]; + } + + if (l2_distance(x_k_1, x_k) <= l2_convergence_tolerance) + break; + } + + free_vector(x_k); + return x_k_1; +} +#+END_SRC + +** Appendix / Miscellaneous +*** Random ++ Author: Elizabeth Hunt ++ Name: ~rand_from~ ++ Location: ~src/rand.c~ ++ Input: a pair of doubles, min and max to generate a random number min + \le x \le max ++ Output: a random double in the constraints shown + +#+BEGIN_SRC c +double rand_from(double min, double max) { + return min + (rand() / (RAND_MAX / (max - min))); +} +#+END_SRC +*** Data Types +**** ~Line~ ++ Author: Elizabeth Hunt ++ Location: ~inc/types.h~ + +#+BEGIN_SRC c +typedef struct Line { + double m; + double a; +} Line; +#+END_SRC +**** The ~Array_<type>~ and ~Matrix_<type>~ ++ Author: Elizabeth Hunt ++ Location: ~inc/types.h~ + +We define two Pre processor Macros ~DEFINE_ARRAY~ and ~DEFINE_MATRIX~ that take +as input a type, and construct a struct definition for the given type for +convenient access to the vector or matrices dimensions. + +Such that ~DEFINE_ARRAY(int)~ would expand to: + +#+BEGIN_SRC c + typedef struct { + int* data; + size_t size; + } Array_int +#+END_SRC + +And ~DEFINE_MATRIX(int)~ would expand a to ~Matrix_int~; containing a pointer to +a collection of pointers of ~Array_int~'s and its dimensions. + +#+BEGIN_SRC c + typedef struct { + Array_int **data; + size_t cols; + size_t rows; + } Matrix_int +#+END_SRC + +*** Macros +**** ~c_max~ and ~c_min~ ++ Author: Elizabeth Hunt ++ Location: ~inc/macros.h~ ++ Input: two structures that define an order measure ++ Output: either the larger or smaller of the two depending on the measure + +#+BEGIN_SRC c +#define c_max(x, y) (((x) >= (y)) ? (x) : (y)) +#define c_min(x, y) (((x) <= (y)) ? (x) : (y)) +#+END_SRC + +**** ~InitArray~ ++ Author: Elizabeth Hunt ++ Location: ~inc/macros.h~ ++ Input: a type and array of values to initialze an array with such type ++ Output: a new ~Array_type~ with the size of the given array and its data + +#+BEGIN_SRC c +#define InitArray(TYPE, ...) \ + ({ \ + TYPE temp[] = __VA_ARGS__; \ + Array_##TYPE *arr = malloc(sizeof(Array_##TYPE)); \ + arr->size = sizeof(temp) / sizeof(temp[0]); \ + arr->data = malloc(arr->size * sizeof(TYPE)); \ + memcpy(arr->data, temp, arr->size * sizeof(TYPE)); \ + arr; \ + }) +#+END_SRC + +**** ~InitArrayWithSize~ ++ Author: Elizabeth Hunt ++ Location: ~inc/macros.h~ ++ Input: a type, a size, and initial value ++ Output: a new ~Array_type~ with the given size filled with the initial value + +#+BEGIN_SRC c +#define InitArrayWithSize(TYPE, SIZE, INIT_VALUE) \ + ({ \ + Array_##TYPE *arr = malloc(sizeof(Array_##TYPE)); \ + arr->size = SIZE; \ + arr->data = malloc(arr->size * sizeof(TYPE)); \ + for (size_t i = 0; i < arr->size; i++) \ + arr->data[i] = INIT_VALUE; \ + arr; \ + }) +#+END_SRC + +**** ~InitMatrixWithSize~ ++ Author: Elizabeth Hunt ++ Location: ~inc/macros.h~ ++ Input: a type, number of rows, columns, and initial value ++ Output: a new ~Matrix_type~ of size ~rows x columns~ filled with the initial + value + +#+BEGIN_SRC c +#define InitMatrixWithSize(TYPE, ROWS, COLS, INIT_VALUE) \ + ({ \ + Matrix_##TYPE *matrix = malloc(sizeof(Matrix_##TYPE)); \ + matrix->rows = ROWS; \ + matrix->cols = COLS; \ + matrix->data = malloc(matrix->rows * sizeof(Array_##TYPE *)); \ + for (size_t y = 0; y < matrix->rows; y++) \ + matrix->data[y] = InitArrayWithSize(TYPE, COLS, INIT_VALUE); \ + matrix; \ + }) +#+END_SRC + diff --git a/Homework/math4610/doc/software_manual.pdf b/Homework/math4610/doc/software_manual.pdf Binary files differnew file mode 100644 index 0000000..1772e7c --- /dev/null +++ b/Homework/math4610/doc/software_manual.pdf diff --git a/Homework/math4610/doc/software_manual.tex b/Homework/math4610/doc/software_manual.tex new file mode 100644 index 0000000..dac099b --- /dev/null +++ b/Homework/math4610/doc/software_manual.tex @@ -0,0 +1,1583 @@ +% Created 2023-12-11 Mon 19:22 +% Intended LaTeX compiler: pdflatex +\documentclass[11pt]{article} +\usepackage[utf8]{inputenc} +\usepackage[T1]{fontenc} +\usepackage{graphicx} +\usepackage{longtable} +\usepackage{wrapfig} +\usepackage{rotating} +\usepackage[normalem]{ulem} +\usepackage{amsmath} +\usepackage{amssymb} +\usepackage{capt-of} +\usepackage{hyperref} +\notindent \notag \usepackage{amsmath} \usepackage[a4paper,margin=1in,portrait]{geometry} +\author{Elizabeth Hunt} +\date{\today} +\title{LIZFCM Software Manual (v0.6)} +\hypersetup{ + pdfauthor={Elizabeth Hunt}, + pdftitle={LIZFCM Software Manual (v0.6)}, + pdfkeywords={}, + pdfsubject={}, + pdfcreator={Emacs 28.2 (Org mode 9.7-pre)}, + pdflang={English}} +\begin{document} + +\maketitle +\tableofcontents + +\setlength\parindent{0pt} + +\section{Design} +\label{sec:org63deaf6} +The LIZFCM static library (at \url{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 \texttt{ASDF} solution that Common Lisp already brings +to the table. + +All of the work established in \texttt{deprecated-cl} has been painstakingly translated into +the C programming language. I have a couple tenets for its design: + +\begin{itemize} +\item Implementations of routines should all be done immutably in respect to arguments. +\item Functional programming is good (it's\ldots{} rough in C though). +\item Routines are separated into "modules" that follow a form of separation of concerns +in files, and not individual files per function. +\end{itemize} + +\section{Compilation} +\label{sec:org7291327} +A provided \texttt{Makefile} is added for convencience. It has been tested on an \texttt{arm}-based M1 machine running +MacOS as well as \texttt{x86} Arch Linux. + +\begin{enumerate} +\item \texttt{cd} into the root of the repo +\item \texttt{make} +\end{enumerate} + +Then, as of homework 5, the testing routines are provided in \texttt{test} and utilize the +\texttt{utest} "micro"library. They compile to a binary in \texttt{./dist/lizfcm.test}. + +Execution of the Makefile will perform compilation of individual routines. + +But, in the requirement of manual intervention (should the little alien workers +inside the computer fail to do their job), one can use the following command to +produce an object file: + +\begin{verbatim} + gcc -Iinc/ -lm -Wall -c src/<the_routine>.c -o build/<the_routine>.o +\end{verbatim} + +Which is then bundled into a static library in \texttt{lib/lizfcm.a} and can be linked +in the standard method. + +\section{The LIZFCM API} +\label{sec:org1ebe7fa} +\subsection{Simple Routines} +\label{sec:orgff18c6b} +\subsubsection{\texttt{smaceps}} +\label{sec:org443df5e} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name: \texttt{smaceps} +\item Location: \texttt{src/maceps.c} +\item Input: none +\item Output: a \texttt{float} returning the specific "Machine Epsilon" of a machine on a +single precision floating point number at which it becomes "indistinguishable". +\end{itemize} + +\begin{verbatim} +float smaceps() { + float one = 1.0; + float machine_epsilon = 1.0; + float one_approx = one + machine_epsilon; + + while (fabsf(one_approx - one) > 0) { + machine_epsilon /= 2; + one_approx = one + machine_epsilon; + } + + return machine_epsilon; +} +\end{verbatim} + +\subsubsection{\texttt{dmaceps}} +\label{sec:org5121603} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name: \texttt{dmaceps} +\item Location: \texttt{src/maceps.c} +\item Input: none +\item Output: a \texttt{double} returning the specific "Machine Epsilon" of a machine on a +double precision floating point number at which it becomes "indistinguishable". +\end{itemize} + +\begin{verbatim} +double dmaceps() { + double one = 1.0; + double machine_epsilon = 1.0; + double one_approx = one + machine_epsilon; + + while (fabs(one_approx - one) > 0) { + machine_epsilon /= 2; + one_approx = one + machine_epsilon; + } + + return machine_epsilon; +} +\end{verbatim} + +\subsection{Derivative Routines} +\label{sec:org6fd324c} +\subsubsection{\texttt{central\_derivative\_at}} +\label{sec:orge9f0821} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name: \texttt{central\_derivative\_at} +\item Location: \texttt{src/approx\_derivative.c} +\item Input: +\begin{itemize} +\item \texttt{f} is a pointer to a one-ary function that takes a double as input and produces +a double as output +\item \texttt{a} is the domain value at which we approximate \texttt{f'} +\item \texttt{h} is the step size +\end{itemize} +\item Output: a \texttt{double} of the approximate value of \texttt{f'(a)} via the central difference +method. +\end{itemize} + +\begin{verbatim} +double central_derivative_at(double (*f)(double), double a, double h) { + assert(h > 0); + + double x2 = a + h; + double x1 = a - h; + + double y2 = f(x2); + double y1 = f(x1); + + return (y2 - y1) / (x2 - x1); +} +\end{verbatim} + +\subsubsection{\texttt{forward\_derivative\_at}} +\label{sec:org8720f28} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name: \texttt{forward\_derivative\_at} +\item Location: \texttt{src/approx\_derivative.c} +\item Input: +\begin{itemize} +\item \texttt{f} is a pointer to a one-ary function that takes a double as input and produces +a double as output +\item \texttt{a} is the domain value at which we approximate \texttt{f'} +\item \texttt{h} is the step size +\end{itemize} +\item Output: a \texttt{double} of the approximate value of \texttt{f'(a)} via the forward difference +method. +\end{itemize} + +\begin{verbatim} +double forward_derivative_at(double (*f)(double), double a, double h) { + assert(h > 0); + + double x2 = a + h; + double x1 = a; + + double y2 = f(x2); + double y1 = f(x1); + + return (y2 - y1) / (x2 - x1); +} +\end{verbatim} + +\subsubsection{\texttt{backward\_derivative\_at}} +\label{sec:org1589b19} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name: \texttt{backward\_derivative\_at} +\item Location: \texttt{src/approx\_derivative.c} +\item Input: +\begin{itemize} +\item \texttt{f} is a pointer to a one-ary function that takes a double as input and produces +a double as output +\item \texttt{a} is the domain value at which we approximate \texttt{f'} +\item \texttt{h} is the step size +\end{itemize} +\item Output: a \texttt{double} of the approximate value of \texttt{f'(a)} via the backward difference +method. +\end{itemize} + +\begin{verbatim} +double backward_derivative_at(double (*f)(double), double a, double h) { + assert(h > 0); + + double x2 = a; + double x1 = a - h; + + double y2 = f(x2); + double y1 = f(x1); + + return (y2 - y1) / (x2 - x1); +} +\end{verbatim} + +\subsection{Vector Routines} +\label{sec:org493841e} +\subsubsection{Vector Arithmetic: \texttt{add\_v, minus\_v}} +\label{sec:org3912c29} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name(s): \texttt{add\_v}, \texttt{minus\_v} +\item Location: \texttt{src/vector.c} +\item Input: two pointers to locations in memory wherein \texttt{Array\_double}'s lie +\item Output: a pointer to a new \texttt{Array\_double} as the result of addition or subtraction +of the two input \texttt{Array\_double}'s +\end{itemize} + +\begin{verbatim} +Array_double *add_v(Array_double *v1, Array_double *v2) { + assert(v1->size == v2->size); + + Array_double *sum = copy_vector(v1); + for (size_t i = 0; i < v1->size; i++) + sum->data[i] += v2->data[i]; + return sum; +} + +Array_double *minus_v(Array_double *v1, Array_double *v2) { + assert(v1->size == v2->size); + + Array_double *sub = InitArrayWithSize(double, v1->size, 0); + for (size_t i = 0; i < v1->size; i++) + sub->data[i] = v1->data[i] - v2->data[i]; + return sub; +} +\end{verbatim} + +\subsubsection{Norms: \texttt{l1\_norm}, \texttt{l2\_norm}, \texttt{linf\_norm}} +\label{sec:orged74cfb} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name(s): \texttt{l1\_norm}, \texttt{l2\_norm}, \texttt{linf\_norm} +\item Location: \texttt{src/vector.c} +\item Input: a pointer to a location in memory wherein an \texttt{Array\_double} lies +\item Output: a \texttt{double} representing the value of the norm the function applies +\end{itemize} + +\begin{verbatim} +double l1_norm(Array_double *v) { + double sum = 0; + for (size_t i = 0; i < v->size; ++i) + sum += fabs(v->data[i]); + return sum; +} + +double l2_norm(Array_double *v) { + double norm = 0; + for (size_t i = 0; i < v->size; ++i) + norm += v->data[i] * v->data[i]; + return sqrt(norm); +} + +double linf_norm(Array_double *v) { + assert(v->size > 0); + double max = v->data[0]; + for (size_t i = 0; i < v->size; ++i) + max = c_max(v->data[i], max); + return max; +} +\end{verbatim} + +\subsubsection{\texttt{vector\_distance}} +\label{sec:org20a5773} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name: \texttt{vector\_distance} +\item Location: \texttt{src/vector.c} +\item Input: two pointers to locations in memory wherein \texttt{Array\_double}'s lie, and a pointer to a +one-ary function \texttt{norm} taking as input a pointer to an \texttt{Array\_double} and returning a double +representing the norm of that \texttt{Array\_double} +\end{itemize} + +\begin{verbatim} +double vector_distance(Array_double *v1, Array_double *v2, + double (*norm)(Array_double *)) { + Array_double *minus = minus_v(v1, v2); + double dist = (*norm)(minus); + free(minus); + return dist; +} +\end{verbatim} + +\subsubsection{Distances: \texttt{l1\_distance}, \texttt{l2\_distance}, \texttt{linf\_distance}} +\label{sec:orgac16178} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name(s): \texttt{l1\_distance}, \texttt{l2\_distance}, \texttt{linf\_distance} +\item Location: \texttt{src/vector.c} +\item Input: two pointers to locations in memory wherein \texttt{Array\_double}'s lie, and the distance +via the corresponding \texttt{l1}, \texttt{l2}, or \texttt{linf} norms +\item Output: A \texttt{double} representing the distance between the two \texttt{Array\_doubles}'s by the given +norm. +\end{itemize} + +\begin{verbatim} +double l1_distance(Array_double *v1, Array_double *v2) { + return vector_distance(v1, v2, &l1_norm); +} + +double l2_distance(Array_double *v1, Array_double *v2) { + return vector_distance(v1, v2, &l2_norm); +} + +double linf_distance(Array_double *v1, Array_double *v2) { + return vector_distance(v1, v2, &linf_norm); +} +\end{verbatim} + +\subsubsection{\texttt{sum\_v}} +\label{sec:org876aafa} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name: \texttt{sum\_v} +\item Location: \texttt{src/vector.c} +\item Input: a pointer to an \texttt{Array\_double} +\item Output: a \texttt{double} representing the sum of all the elements of an \texttt{Array\_double} +\end{itemize} + +\begin{verbatim} +double sum_v(Array_double *v) { + double sum = 0; + for (size_t i = 0; i < v->size; i++) + sum += v->data[i]; + return sum; +} +\end{verbatim} + +\subsubsection{\texttt{scale\_v}} +\label{sec:orgf1d236c} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name: \texttt{scale\_v} +\item Location: \texttt{src/vector.c} +\item Input: a pointer to an \texttt{Array\_double} and a scalar \texttt{double} to scale the vector +\item Output: a pointer to a new \texttt{Array\_double} of the scaled input \texttt{Array\_double} +\end{itemize} + +\begin{verbatim} +Array_double *scale_v(Array_double *v, double m) { + Array_double *copy = copy_vector(v); + for (size_t i = 0; i < v->size; i++) + copy->data[i] *= m; + return copy; +} +\end{verbatim} + +\subsubsection{\texttt{free\_vector}} +\label{sec:org2ca163d} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name: \texttt{free\_vector} +\item Location: \texttt{src/vector.c} +\item Input: a pointer to an \texttt{Array\_double} +\item Output: nothing. +\item Side effect: free the memory of the reserved \texttt{Array\_double} on the heap +\end{itemize} + +\begin{verbatim} +void free_vector(Array_double *v) { + free(v->data); + free(v); +} +\end{verbatim} + +\subsubsection{\texttt{add\_element}} +\label{sec:org7a99233} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name: \texttt{add\_element} +\item Location: \texttt{src/vector.c} +\item Input: a pointer to an \texttt{Array\_double} +\item Output: a new \texttt{Array\_double} with element \texttt{x} appended. +\end{itemize} + +\begin{verbatim} +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{verbatim} + +\subsubsection{\texttt{slice\_element}} +\label{sec:org6c07c99} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name: \texttt{slice\_element} +\item Location: \texttt{src/vector.c} +\item Input: a pointer to an \texttt{Array\_double} +\item Output: a new \texttt{Array\_double} with element \texttt{x} sliced. +\end{itemize} + +\begin{verbatim} +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{verbatim} + +\subsubsection{\texttt{copy\_vector}} +\label{sec:org81f7cc1} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name: \texttt{copy\_vector} +\item Location: \texttt{src/vector.c} +\item Input: a pointer to an \texttt{Array\_double} +\item Output: a pointer to a new \texttt{Array\_double} whose \texttt{data} and \texttt{size} are copied from the input +\texttt{Array\_double} +\end{itemize} + +\begin{verbatim} +Array_double *copy_vector(Array_double *v) { + Array_double *copy = InitArrayWithSize(double, v->size, 0.0); + for (size_t i = 0; i < copy->size; ++i) + copy->data[i] = v->data[i]; + return copy; +} +\end{verbatim} + +\subsubsection{\texttt{format\_vector\_into}} +\label{sec:orgd168171} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name: \texttt{format\_vector\_into} +\item Location: \texttt{src/vector.c} +\item Input: a pointer to an \texttt{Array\_double} and a pointer to a c-string \texttt{s} to "print" the vector out +into +\item Output: nothing. +\item Side effect: overwritten memory into \texttt{s} +\end{itemize} + +\begin{verbatim} +void format_vector_into(Array_double *v, char *s) { + if (v->size == 0) { + strcat(s, "empty"); + return; + } + + for (size_t i = 0; i < v->size; ++i) { + char num[64]; + strcpy(num, ""); + + sprintf(num, "%f,", v->data[i]); + strcat(s, num); + } + strcat(s, "\n"); +} +\end{verbatim} + +\subsection{Matrix Routines} +\label{sec:org5c45c12} +\subsubsection{\texttt{lu\_decomp}} +\label{sec:orgf1e0ac3} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name: \texttt{lu\_decomp} +\item Location: \texttt{src/matrix.c} +\item Input: a pointer to a \texttt{Matrix\_double} \(m\) to decompose into a lower triangular and upper triangular +matrix \(L\), \(U\), respectively such that \(LU = m\). +\item Output: a pointer to the location in memory in which two \texttt{Matrix\_double}'s reside: the first +representing \(L\), the second, \(U\). +\item Errors: Fails assertions when encountering a matrix that cannot be +decomposed +\end{itemize} + +\begin{verbatim} +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_matrix(l_empt); + + 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"); + 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 (denom == 0) { + printf("ERROR: non-factorable matrix\n"); + assert(false); + } + + 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]); + + u->data[y] = added; + l->data[y]->data[x] = -factor; + } + } + } + + u_l[0] = u; + u_l[1] = l; + return u_l; +} +\end{verbatim} +\subsubsection{\texttt{bsubst}} +\label{sec:orgec7e4b5} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name: \texttt{bsubst} +\item Location: \texttt{src/matrix.c} +\item Input: a pointer to an upper-triangular \texttt{Matrix\_double} \(u\) and a \texttt{Array\_double} +\(b\) +\item Output: a pointer to a new \texttt{Array\_double} whose entries are given by performing +back substitution +\end{itemize} + +\begin{verbatim} +Array_double *bsubst(Matrix_double *u, Array_double *b) { + assert(u->rows == b->size && u->cols == u->rows); + + Array_double *x = copy_vector(b); + for (int64_t row = b->size - 1; row >= 0; row--) { + for (size_t col = b->size - 1; col > row; col--) + x->data[row] -= x->data[col] * u->data[row]->data[col]; + x->data[row] /= u->data[row]->data[row]; + } + return x; +} +\end{verbatim} +\subsubsection{\texttt{fsubst}} +\label{sec:org72ff2ed} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name: \texttt{fsubst} +\item Location: \texttt{src/matrix.c} +\item Input: a pointer to a lower-triangular \texttt{Matrix\_double} \(l\) and a \texttt{Array\_double} +\(b\) +\item Output: a pointer to a new \texttt{Array\_double} whose entries are given by performing +forward substitution +\end{itemize} + +\begin{verbatim} +Array_double *fsubst(Matrix_double *l, Array_double *b) { + assert(l->rows == b->size && l->cols == l->rows); + + Array_double *x = copy_vector(b); + + for (size_t row = 0; row < b->size; row++) { + for (size_t col = 0; col < row; col++) + x->data[row] -= x->data[col] * l->data[row]->data[col]; + x->data[row] /= l->data[row]->data[row]; + } + + return x; +} +\end{verbatim} + +\subsubsection{\texttt{solve\_matrix\_lu\_bsubst}} +\label{sec:orga735557} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Location: \texttt{src/matrix.c} +\item Input: a pointer to a \texttt{Matrix\_double} \(m\) and a pointer to an \texttt{Array\_double} \(b\) +\item Output: \(x\) such that \(mx = b\) if such a solution exists (else it's non LU-factorable as discussed +above) +\end{itemize} + +Here we make use of forward substitution to first solve \(Ly = b\) given \(L\) as the \(L\) factor in +\texttt{lu\_decomp}. Then we use back substitution to solve \(Ux = y\) for \(x\) similarly given \(U\). + +Then, \(LUx = b\), thus \(x\) is a solution. + +\begin{verbatim} +Array_double *solve_matrix_lu_bsubst(Matrix_double *m, Array_double *b) { + assert(b->size == m->rows); + assert(m->rows == m->cols); + + Array_double *x = copy_vector(b); + Matrix_double **u_l = lu_decomp(m); + Matrix_double *u = u_l[0]; + Matrix_double *l = u_l[1]; + + Array_double *b_fsub = fsubst(l, b); + x = bsubst(u, b_fsub); + free_vector(b_fsub); + + free_matrix(u); + free_matrix(l); + free(u_l); + + return x; +} +\end{verbatim} + +\subsubsection{\texttt{gaussian\_elimination}} +\label{sec:org71d2519} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Location: \texttt{src/matrix.c} +\item Input: a pointer to a \texttt{Matrix\_double} \(m\) +\item Output: a pointer to a copy of \(m\) in reduced echelon form +\end{itemize} + +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 \url{https://en.wikipedia.org/wiki/Gaussian\_elimination}. + +\begin{verbatim} +Matrix_double *gaussian_elimination(Matrix_double *m) { + uint64_t h = 0, k = 0; + + Matrix_double *m_cp = copy_matrix(m); + + while (h < m_cp->rows && k < m_cp->cols) { + uint64_t max_row = h; + double max_val = 0.0; + + for (uint64_t row = h; row < m_cp->rows; row++) { + double val = fabs(m_cp->data[row]->data[k]); + if (val > max_val) { + max_val = val; + max_row = row; + } + } + + if (max_val == 0.0) { + k++; + continue; + } + + if (max_row != h) { + 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{verbatim} + +\subsubsection{\texttt{solve\_matrix\_gaussian}} +\label{sec:org230915f} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Location: \texttt{src/matrix.c} +\item Input: a pointer to a \texttt{Matrix\_double} \(m\) and a target \texttt{Array\_double} \(b\) +\item Output: a pointer to a vector \(x\) being the solution to the equation \(mx = b\) +\end{itemize} + +We first perform \texttt{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{verbatim} +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{verbatim} + + +\subsubsection{\texttt{m\_dot\_v}} +\label{sec:org83c8351} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Location: \texttt{src/matrix.c} +\item Input: a pointer to a \texttt{Matrix\_double} \(m\) and \texttt{Array\_double} \(v\) +\item Output: the dot product \(mv\) as an \texttt{Array\_double} +\end{itemize} + +\begin{verbatim} +Array_double *m_dot_v(Matrix_double *m, Array_double *v) { + assert(v->size == m->cols); + + Array_double *product = copy_vector(v); + + for (size_t row = 0; row < v->size; ++row) + product->data[row] = v_dot_v(m->data[row], v); + + return product; +} +\end{verbatim} + +\subsubsection{\texttt{put\_identity\_diagonal}} +\label{sec:orge3fcb3e} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Location: \texttt{src/matrix.c} +\item Input: a pointer to a \texttt{Matrix\_double} +\item Output: a pointer to a copy to \texttt{Matrix\_double} whose diagonal is full of 1's +\end{itemize} + +\begin{verbatim} +Matrix_double *put_identity_diagonal(Matrix_double *m) { + assert(m->rows == m->cols); + Matrix_double *copy = copy_matrix(m); + for (size_t y = 0; y < m->rows; ++y) + copy->data[y]->data[y] = 1.0; + return copy; +} +\end{verbatim} + +\subsubsection{\texttt{slice\_column}} +\label{sec:org95e39ba} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Location: \texttt{src/matrix.c} +\item Input: a pointer to a \texttt{Matrix\_double} +\item Output: a pointer to a copy of the given \texttt{Matrix\_double} with column at \texttt{x} sliced +\end{itemize} + +\begin{verbatim} +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{verbatim} + +\subsubsection{\texttt{add\_column}} +\label{sec:org9a2ad93} +\begin{itemize} +\item Author: Elizabet Hunt +\item Location: \texttt{src/matrix.c} +\item Input: a pointer to a \texttt{Matrix\_double} and a new vector representing the appended column \texttt{x} +\item Output: a pointer to a copy of the given \texttt{Matrix\_double} with a new column \texttt{x} +\end{itemize} + +\begin{verbatim} +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{verbatim} + +\subsubsection{\texttt{copy\_matrix}} +\label{sec:org63765c0} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Location: \texttt{src/matrix.c} +\item Input: a pointer to a \texttt{Matrix\_double} +\item Output: a pointer to a copy of the given \texttt{Matrix\_double} +\end{itemize} + +\begin{verbatim} +Matrix_double *copy_matrix(Matrix_double *m) { + Matrix_double *copy = InitMatrixWithSize(double, m->rows, m->cols, 0.0); + for (size_t y = 0; y < copy->rows; y++) { + free_vector(copy->data[y]); + copy->data[y] = copy_vector(m->data[y]); + } + return copy; +} +\end{verbatim} + +\subsubsection{\texttt{free\_matrix}} +\label{sec:orgc337967} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Location: \texttt{src/matrix.c} +\item Input: a pointer to a \texttt{Matrix\_double} +\item Output: none. +\item Side Effects: frees memory reserved by a given \texttt{Matrix\_double} and its member +\texttt{Array\_double} vectors describing its rows. +\end{itemize} + +\begin{verbatim} +void free_matrix(Matrix_double *m) { + for (size_t y = 0; y < m->rows; ++y) + free_vector(m->data[y]); + free(m); +} +\end{verbatim} + +\subsubsection{\texttt{format\_matrix\_into}} +\label{sec:org6b188b4} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name: \texttt{format\_matrix\_into} +\item Location: \texttt{src/matrix.c} +\item Input: a pointer to a \texttt{Matrix\_double} and a pointer to a c-string \texttt{s} to "print" the vector out +into +\item Output: nothing. +\item Side effect: overwritten memory into \texttt{s} +\end{itemize} + +\begin{verbatim} +void format_matrix_into(Matrix_double *m, char *s) { + if (m->rows == 0) + strcpy(s, "empty"); + + for (size_t y = 0; y < m->rows; ++y) { + char row_s[5192]; + strcpy(row_s, ""); + + format_vector_into(m->data[y], row_s); + strcat(s, row_s); + } + strcat(s, "\n"); +} +\end{verbatim} +\subsection{Root Finding Methods} +\label{sec:org352ccdf} +\subsubsection{\texttt{find\_ivt\_range}} +\label{sec:orgb9a0d16} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name: \texttt{find\_ivt\_range} +\item Location: \texttt{src/roots.c} +\item Input: a pointer to a oneary function taking a double and producing a double, the beginning point +in \(R\) to search for a range, a \texttt{delta} step that is taken, and a \texttt{max\_steps} number of maximum +iterations to perform. +\item Output: a pair of \texttt{double}'s in an \texttt{Array\_double} representing a closed closed interval \texttt{[beginning, end]} +\end{itemize} + +\begin{verbatim} +// f is well defined at start_x + delta*n for all n on the integer range [0, +// max_iterations] +Array_double *find_ivt_range(double (*f)(double), double start_x, double delta, + size_t max_iterations) { + double a = start_x; + + while (f(a) * f(a + delta) >= 0 && max_iterations > 0) { + max_iterations--; + a += delta; + } + + double end = a + delta; + double begin = a - delta; + + if (max_iterations == 0 && f(begin) * f(end) >= 0) + return NULL; + return InitArray(double, {begin, end}); +} +\end{verbatim} +\subsubsection{\texttt{bisect\_find\_root}} +\label{sec:org25382b3} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name(s): \texttt{bisect\_find\_root} +\item Input: a one-ary function taking a double and producing a double, a closed interval represented +by \texttt{a} and \texttt{b}: \texttt{[a, b]}, a \texttt{tolerance} at which we return the estimated root once \(b-a < \text{tolerance}\), and a +\texttt{max\_iterations} to break us out of a loop if we can never reach the \texttt{tolerance}. +\item Output: a vector of size of 3, \texttt{double}'s representing first the range \texttt{[a,b]} and then the midpoint, +\texttt{c} of the range. +\item Description: recursively uses binary search to split the interval until we reach \texttt{tolerance}. We +also assume the function \texttt{f} is continuous on \texttt{[a, b]}. +\end{itemize} + +\begin{verbatim} +// f is continuous on [a, b] +Array_double *bisect_find_root(double (*f)(double), double a, double b, + double tolerance, size_t max_iterations) { + assert(a <= b); + // guarantee there's a root somewhere between a and b by IVT + assert(f(a) * f(b) < 0); + + double c = (1.0 / 2) * (a + b); + if (b - a < tolerance || max_iterations == 0) + return InitArray(double, {a, b, c}); + + if (f(a) * f(c) < 0) + return bisect_find_root(f, a, c, tolerance, max_iterations - 1); + return bisect_find_root(f, c, b, tolerance, max_iterations - 1); +} +\end{verbatim} +\subsubsection{\texttt{bisect\_find\_root\_with\_error\_assumption}} +\label{sec:org4b9cb72} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name: \texttt{bisect\_find\_root\_with\_error\_assumption} +\item Input: a one-ary function taking a double and producing a double, a closed interval represented +by \texttt{a} and \texttt{b}: \texttt{[a, b]}, and a \texttt{tolerance} equivalent to the above definition in \texttt{bisect\_find\_root} +\item Output: a \texttt{double} representing the estimated root +\item Description: using the bisection method we know that \(e_k \le (\frac{1}{2})^k (b_0 - a_0)\). So we can +calculate \(k\) at the worst possible case (that the error is exactly the tolerance) to be +\(\frac{log(tolerance) - log(b_0 - a_0)}{log(\frac{1}{2})}\). We pass this value into the \texttt{max\_iterations} +of \texttt{bisect\_find\_root} as above. +\end{itemize} +\begin{verbatim} +double bisect_find_root_with_error_assumption(double (*f)(double), double a, + double b, double tolerance) { + assert(a <= b); + + uint64_t max_iterations = + (uint64_t)ceil((log(tolerance) - log(b - a)) / log(1 / 2.0)); + + Array_double *a_b_root = bisect_find_root(f, a, b, tolerance, max_iterations); + double root = a_b_root->data[2]; + free_vector(a_b_root); + + return root; +} +\end{verbatim} + +\subsubsection{\texttt{fixed\_point\_iteration\_method}} +\label{sec:org4cee2bd} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name: \texttt{fixed\_point\_iteration\_method} +\item Location: \texttt{src/roots.c} +\item Input: a pointer to a oneary function \(f\) taking a double and producing a double of which we are +trying to find a root, a guess \(x_0\), and a function \(g\) of the same signature of \(f\) at which we +"step" our guesses according to the fixed point iteration method: \(x_k = g(x_{k-1})\). Additionally, a +\texttt{max\_iterations} representing the maximum number of "steps" to take before arriving at our +approximation and a \texttt{tolerance} to return our root if it becomes within [0 - tolerance, 0 + tolerance]. +\item Assumptions: \(g(x)\) must be a function such that at the point \(x^*\) (the found root) the derivative +\(|g'(x^*)| \lt 1\) +\item Output: a double representing the found approximate root \(\approx x^*\). +\end{itemize} + +\begin{verbatim} +double fixed_point_iteration_method(double (*f)(double), double (*g)(double), + double x_0, double tolerance, + size_t max_iterations) { + if (max_iterations <= 0) + return x_0; + + double root = g(x_0); + if (tolerance >= fabs(f(root))) + return root; + + return fixed_point_iteration_method(f, g, root, tolerance, + max_iterations - 1); +} +\end{verbatim} + +\subsubsection{\texttt{fixed\_point\_newton\_method}} +\label{sec:org93e3999} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name: \texttt{fixed\_point\_newton\_method} +\item Location: \texttt{src/roots.c} +\item Input: a pointer to a oneary function \(f\) taking a double and producing a double of which we are +trying to find a root and another pointer to a function fprime of the same signature, a guess \(x_0\), +and a \texttt{max\_iterations} and \texttt{tolerance} as defined in the above method are required inputs. +\item Description: continually computes elements in the sequence \(x_n = x_{n-1} - \frac{f(x_{n-1})}{f'p(x_{n-1})}\) +\item Output: a double representing the found approximate root \(\approx x^*\) recursively applied to the sequence +given +\end{itemize} +\begin{verbatim} +double fixed_point_newton_method(double (*f)(double), double (*fprime)(double), + double x_0, double tolerance, + size_t max_iterations) { + if (max_iterations <= 0) + return x_0; + + double root = x_0 - f(x_0) / fprime(x_0); + if (tolerance >= fabs(f(root))) + return root; + + return fixed_point_newton_method(f, fprime, root, tolerance, + max_iterations - 1); +} +\end{verbatim} + +\subsubsection{\texttt{fixed\_point\_secant\_method}} +\label{sec:orgf3f0711} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name: \texttt{fixed\_point\_secant\_method} +\item Location: \texttt{src/roots.c} +\item Input: a pointer to a oneary function \(f\) taking a double and producing a double of which we are +trying to find a root, a guess \(x_0\) and \(x_1\) in which a root lies between \([x_0, x_1]\); applying the +sequence \(x_n = x_{n-1} - f(x_{n-1}) \frac{x_{n-1} - x_{n-2}}{f(x_{n-1}) - f(x_{n-2})}\). +Additionally, a \texttt{max\_iterations} and \texttt{tolerance} as defined in the above method are required +inputs. +\item Output: a double representing the found approximate root \(\approx x^*\) recursively applied to the sequence. +\end{itemize} +\begin{verbatim} +double fixed_point_secant_method(double (*f)(double), double x_0, double x_1, + double tolerance, size_t max_iterations) { + if (max_iterations == 0) + return x_1; + + double root = x_1 - f(x_1) * ((x_1 - x_0) / (f(x_1) - f(x_0))); + + if (tolerance >= fabs(f(root))) + return root; + + return fixed_point_secant_method(f, x_1, root, tolerance, max_iterations - 1); +} +\end{verbatim} +\subsubsection{\texttt{fixed\_point\_secant\_bisection\_method}} +\label{sec:orgeaef048} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name: \texttt{fixed\_point\_secant\_method} +\item Location: \texttt{src/roots.c} +\item Input: a pointer to a oneary function \(f\) taking a double and producing a double of which we are +trying to find a root, a guess \(x_0\), and a \(x_1\) of which we define our first interval \([x_0, x_1]\). +Then, we perform a single iteration of the \texttt{fixed\_point\_secant\_method} on this interval; if it +produces a root outside, we refresh the interval and root respectively with the given +\texttt{bisect\_find\_root} method. Additionally, a \texttt{max\_iterations} and \texttt{tolerance} as defined in the above method are required +inputs. +\item Output: a double representing the found approximate root \(\approx x^*\) continually applied with the +constraints defined. +\end{itemize} + +\begin{verbatim} +double fixed_point_secant_bisection_method(double (*f)(double), double x_0, + double x_1, double tolerance, + size_t max_iterations) { + double begin = x_0; + double end = x_1; + double root = x_0; + + while (tolerance < fabs(f(root)) && max_iterations > 0) { + max_iterations--; + + double secant_root = fixed_point_secant_method(f, begin, end, tolerance, 1); + + if (secant_root < begin || secant_root > end) { + Array_double *range_root = bisect_find_root(f, begin, end, tolerance, 1); + + begin = range_root->data[0]; + end = range_root->data[1]; + root = range_root->data[2]; + + free_vector(range_root); + continue; + } + + root = secant_root; + + if (f(root) * f(begin) < 0) + end = secant_root; // the root exists in [begin, secant_root] + else + begin = secant_root; + } + + return root; +} +\end{verbatim} + +\subsection{Linear Routines} +\label{sec:orge3b6d97} +\subsubsection{\texttt{least\_squares\_lin\_reg}} +\label{sec:orgcc90c4a} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name: \texttt{least\_squares\_lin\_reg} +\item Location: \texttt{src/lin.c} +\item Input: two pointers to \texttt{Array\_double}'s whose entries correspond two ordered +pairs in R\textsuperscript{2} +\item Output: a linear model best representing the ordered pairs via least squares +regression +\end{itemize} + +\begin{verbatim} +Line *least_squares_lin_reg(Array_double *x, Array_double *y) { + assert(x->size == y->size); + + uint64_t n = x->size; + double sum_x = sum_v(x); + double sum_y = sum_v(y); + double sum_xy = v_dot_v(x, y); + double sum_xx = v_dot_v(x, x); + double denom = ((n * sum_xx) - (sum_x * sum_x)); + + Line *line = malloc(sizeof(Line)); + line->m = ((sum_xy * n) - (sum_x * sum_y)) / denom; + line->a = ((sum_y * sum_xx) - (sum_x * sum_xy)) / denom; + + return line; +} +\end{verbatim} + +\subsection{Eigen-Adjacent} +\label{sec:orga3c637f} +\subsubsection{\texttt{dominant\_eigenvalue}} +\label{sec:org0306c8a} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name: \texttt{dominant\_eigenvalue} +\item Location: \texttt{src/eigen.c} +\item Input: a pointer to an invertible matrix \texttt{m}, an initial eigenvector guess \texttt{v} (that is non +zero or orthogonal to an eigenvector with the dominant eigenvalue), a \texttt{tolerance} and +\texttt{max\_iterations} that act as stop conditions +\item Output: the dominant eigenvalue with the highest magnitude, approximated with the Power +Iteration Method +\end{itemize} + +\begin{verbatim} +double dominant_eigenvalue(Matrix_double *m, Array_double *v, double tolerance, + size_t max_iterations) { + assert(m->rows == m->cols); + assert(m->rows == v->size); + + double error = tolerance; + size_t iter = max_iterations; + double lambda = 0.0; + Array_double *eigenvector_1 = copy_vector(v); + + while (error >= tolerance && (--iter) > 0) { + Array_double *eigenvector_2 = m_dot_v(m, eigenvector_1); + Array_double *normalized_eigenvector_2 = + scale_v(eigenvector_2, 1.0 / linf_norm(eigenvector_2)); + free_vector(eigenvector_2); + eigenvector_2 = normalized_eigenvector_2; + + Array_double *mx = m_dot_v(m, eigenvector_2); + double new_lambda = + v_dot_v(mx, eigenvector_2) / v_dot_v(eigenvector_2, eigenvector_2); + + error = fabs(new_lambda - lambda); + lambda = new_lambda; + free_vector(eigenvector_1); + eigenvector_1 = eigenvector_2; + } + + return lambda; +} +\end{verbatim} +\subsubsection{\texttt{shift\_inverse\_power\_eigenvalue}} +\label{sec:orgc29637a} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name: \texttt{least\_dominant\_eigenvalue} +\item Location: \texttt{src/eigen.c} +\item Input: a pointer to an invertible matrix \texttt{m}, an initial eigenvector guess \texttt{v} (that is non +zero or orthogonal to an eigenvector with the dominant eigenvalue), a \texttt{shift} to act as the +shifted \(\delta\), and \texttt{tolerance} and \texttt{max\_iterations} that act as stop conditions. +\item Output: the eigenvalue closest to \texttt{shift} with the lowest magnitude closest to 0, approximated +with the Inverse Power Iteration Method +\end{itemize} +\begin{verbatim} +double shift_inverse_power_eigenvalue(Matrix_double *m, Array_double *v, + double shift, double tolerance, + size_t max_iterations) { + assert(m->rows == m->cols); + assert(m->rows == v->size); + + Matrix_double *m_c = copy_matrix(m); + for (size_t y = 0; y < m_c->rows; ++y) + m_c->data[y]->data[y] = m_c->data[y]->data[y] - shift; + + double error = tolerance; + size_t iter = max_iterations; + double lambda = shift; + Array_double *eigenvector_1 = copy_vector(v); + + while (error >= tolerance && (--iter) > 0) { + Array_double *eigenvector_2 = solve_matrix_lu_bsubst(m_c, eigenvector_1); + Array_double *normalized_eigenvector_2 = + scale_v(eigenvector_2, 1.0 / linf_norm(eigenvector_2)); + free_vector(eigenvector_2); + + Array_double *mx = m_dot_v(m, normalized_eigenvector_2); + double new_lambda = + v_dot_v(mx, normalized_eigenvector_2) / + v_dot_v(normalized_eigenvector_2, normalized_eigenvector_2); + + error = fabs(new_lambda - lambda); + lambda = new_lambda; + free_vector(eigenvector_1); + eigenvector_1 = normalized_eigenvector_2; + } + + return lambda; +} +\end{verbatim} + +\subsubsection{\texttt{least\_dominant\_eigenvalue}} +\label{sec:org5df73a2} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name: \texttt{least\_dominant\_eigenvalue} +\item Location: \texttt{src/eigen.c} +\item Input: a pointer to an invertible matrix \texttt{m}, an initial eigenvector guess \texttt{v} (that is non +zero or orthogonal to an eigenvector with the dominant eigenvalue), a \texttt{tolerance} and +\texttt{max\_iterations} that act as stop conditions. +\item Output: the least dominant eigenvalue with the lowest magnitude closest to 0, approximated +with the Inverse Power Iteration Method. +\end{itemize} +\begin{verbatim} +double least_dominant_eigenvalue(Matrix_double *m, Array_double *v, + double tolerance, size_t max_iterations) { + return shift_inverse_power_eigenvalue(m, v, 0.0, tolerance, max_iterations); +} +\end{verbatim} +\subsubsection{\texttt{partition\_find\_eigenvalues}} +\label{sec:org3dde7af} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name: \texttt{partition\_find\_eigenvalues} +\item Location: \texttt{src/eigen.c} +\item Input: a pointer to an invertible matrix \texttt{m}, a matrix whose rows correspond to initial +eigenvector guesses at each "partition" which is computed from a uniform distribution +between the number of rows this "guess matrix" has and the distance between the least +dominant eigenvalue and the most dominant. Additionally, a \texttt{max\_iterations} and a \texttt{tolerance} +that act as stop conditions. +\item Output: a vector of \texttt{doubles} corresponding to the "nearest" eigenvalue at the midpoint of +each partition, via the given guess of that partition. +\end{itemize} +\begin{verbatim} +Array_double *partition_find_eigenvalues(Matrix_double *m, + Matrix_double *guesses, + double tolerance, + size_t max_iterations) { + assert(guesses->rows >= + 2); // we need at least, the most and least dominant eigenvalues + + double end = dominant_eigenvalue(m, guesses->data[guesses->rows - 1], + tolerance, max_iterations); + double begin = + least_dominant_eigenvalue(m, guesses->data[0], tolerance, max_iterations); + + double delta = (end - begin) / guesses->rows; + Array_double *eigenvalues = InitArrayWithSize(double, guesses->rows, 0.0); + for (size_t i = 0; i < guesses->rows; i++) { + double box_midpoint = ((delta * i) + (delta * (i + 1))) / 2; + + double nearest_eigenvalue = shift_inverse_power_eigenvalue( + m, guesses->data[i], box_midpoint, tolerance, max_iterations); + + eigenvalues->data[i] = nearest_eigenvalue; + } + + return eigenvalues; +} +\end{verbatim} +\subsubsection{\texttt{leslie\_matrix}} +\label{sec:orgca10ed3} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name: \texttt{leslie\_matrix} +\item Location: \texttt{src/eigen.c} +\item Input: two pointers to \texttt{Array\_double}'s representing the ratio of individuals in an age class +\(x\) getting to the next age class \(x+1\) and the number of offspring that individuals in an age +class create in age class 0. +\item Output: the leslie matrix generated from the input vectors. +\end{itemize} + +\begin{verbatim} +Matrix_double *leslie_matrix(Array_double *age_class_surivor_ratio, + Array_double *age_class_offspring) { + assert(age_class_surivor_ratio->size + 1 == age_class_offspring->size); + + Matrix_double *leslie = InitMatrixWithSize(double, age_class_offspring->size, + age_class_offspring->size, 0.0); + + free_vector(leslie->data[0]); + leslie->data[0] = age_class_offspring; + + for (size_t i = 0; i < age_class_surivor_ratio->size; i++) + leslie->data[i + 1]->data[i] = age_class_surivor_ratio->data[i]; + return leslie; +} +\end{verbatim} +\subsection{Jacobi / Gauss-Siedel} +\label{sec:org91c563c} +\subsubsection{\texttt{jacobi\_solve}} +\label{sec:org2cd6098} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name: \texttt{jacobi\_solve} +\item Location: \texttt{src/matrix.c} +\item Input: a pointer to a diagonally dominant square matrix \(m\), a vector representing +the value \(b\) in \(mx = b\), a double representing the maximum distance between +the solutions produced by iteration \(i\) and \(i+1\) (by L2 norm a.k.a cartesian +distance), and a \texttt{max\_iterations} which we force stop. +\item Output: the converged-upon solution \(x\) to \(mx = b\) +\end{itemize} +\begin{verbatim} +Array_double *jacobi_solve(Matrix_double *m, Array_double *b, + double l2_convergence_tolerance, + size_t max_iterations) { + assert(m->rows == m->cols); + assert(b->size == m->cols); + size_t iter = max_iterations; + + Array_double *x_k = InitArrayWithSize(double, b->size, 0.0); + Array_double *x_k_1 = + InitArrayWithSize(double, b->size, rand_from(0.1, 10.0)); + + while ((--iter) > 0 && l2_distance(x_k_1, x_k) > l2_convergence_tolerance) { + for (size_t i = 0; i < m->rows; i++) { + double delta = 0.0; + for (size_t j = 0; j < m->cols; j++) { + if (i == j) + continue; + delta += m->data[i]->data[j] * x_k->data[j]; + } + x_k_1->data[i] = (b->data[i] - delta) / m->data[i]->data[i]; + } + + Array_double *tmp = x_k; + x_k = x_k_1; + x_k_1 = tmp; + } + + free_vector(x_k); + return x_k_1; +} +\end{verbatim} + +\subsubsection{\texttt{gauss\_siedel\_solve}} +\label{sec:org6633923} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name: \texttt{gauss\_siedel\_solve} +\item Location: \texttt{src/matrix.c} +\item Input: a pointer to a \href{https://en.wikipedia.org/wiki/Gauss\%E2\%80\%93Seidel\_method}{diagonally dominant or symmetric and positive definite} +square matrix \(m\), a vector representing +the value \(b\) in \(mx = b\), a double representing the maximum distance between +the solutions produced by iteration \(i\) and \(i+1\) (by L2 norm a.k.a cartesian +distance), and a \texttt{max\_iterations} which we force stop. +\item Output: the converged-upon solution \(x\) to \(mx = b\) +\item Description: we use almost the exact same method as \texttt{jacobi\_solve} but modify +only one array in accordance to the Gauss-Siedel method, but which is necessarily +copied before due to the convergence check. +\end{itemize} +\begin{verbatim} + +Array_double *gauss_siedel_solve(Matrix_double *m, Array_double *b, + double l2_convergence_tolerance, + size_t max_iterations) { + assert(m->rows == m->cols); + assert(b->size == m->cols); + size_t iter = max_iterations; + + Array_double *x_k = InitArrayWithSize(double, b->size, 0.0); + Array_double *x_k_1 = + InitArrayWithSize(double, b->size, rand_from(0.1, 10.0)); + + while ((--iter) > 0) { + for (size_t i = 0; i < x_k->size; i++) + x_k->data[i] = x_k_1->data[i]; + + for (size_t i = 0; i < m->rows; i++) { + double delta = 0.0; + for (size_t j = 0; j < m->cols; j++) { + if (i == j) + continue; + delta += m->data[i]->data[j] * x_k_1->data[j]; + } + x_k_1->data[i] = (b->data[i] - delta) / m->data[i]->data[i]; + } + + if (l2_distance(x_k_1, x_k) <= l2_convergence_tolerance) + break; + } + + free_vector(x_k); + return x_k_1; +} +\end{verbatim} + +\subsection{Appendix / Miscellaneous} +\label{sec:orga72494e} +\subsubsection{Random} +\label{sec:org4940c39} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Name: \texttt{rand\_from} +\item Location: \texttt{src/rand.c} +\item Input: a pair of doubles, min and max to generate a random number min +\(\le\) x \(\le\) max +\item Output: a random double in the constraints shown +\end{itemize} + +\begin{verbatim} +double rand_from(double min, double max) { + return min + (rand() / (RAND_MAX / (max - min))); +} +\end{verbatim} +\subsubsection{Data Types} +\label{sec:org8d3f6e1} +\begin{enumerate} +\item \texttt{Line} +\label{sec:orgc0df901} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Location: \texttt{inc/types.h} +\end{itemize} + +\begin{verbatim} +typedef struct Line { + double m; + double a; +} Line; +\end{verbatim} +\item The \texttt{Array\_<type>} and \texttt{Matrix\_<type>} +\label{sec:org435e816} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Location: \texttt{inc/types.h} +\end{itemize} + +We define two Pre processor Macros \texttt{DEFINE\_ARRAY} and \texttt{DEFINE\_MATRIX} that take +as input a type, and construct a struct definition for the given type for +convenient access to the vector or matrices dimensions. + +Such that \texttt{DEFINE\_ARRAY(int)} would expand to: + +\begin{verbatim} +typedef struct { + int* data; + size_t size; +} Array_int +\end{verbatim} + +And \texttt{DEFINE\_MATRIX(int)} would expand a to \texttt{Matrix\_int}; containing a pointer to +a collection of pointers of \texttt{Array\_int}'s and its dimensions. + +\begin{verbatim} +typedef struct { + Array_int **data; + size_t cols; + size_t rows; +} Matrix_int +\end{verbatim} +\end{enumerate} + +\subsubsection{Macros} +\label{sec:orga2161be} +\begin{enumerate} +\item \texttt{c\_max} and \texttt{c\_min} +\label{sec:org16ca9c3} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Location: \texttt{inc/macros.h} +\item Input: two structures that define an order measure +\item Output: either the larger or smaller of the two depending on the measure +\end{itemize} + +\begin{verbatim} +#define c_max(x, y) (((x) >= (y)) ? (x) : (y)) +#define c_min(x, y) (((x) <= (y)) ? (x) : (y)) +\end{verbatim} + +\item \texttt{InitArray} +\label{sec:orgcaff993} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Location: \texttt{inc/macros.h} +\item Input: a type and array of values to initialze an array with such type +\item Output: a new \texttt{Array\_type} with the size of the given array and its data +\end{itemize} + +\begin{verbatim} +#define InitArray(TYPE, ...) \ + ({ \ + TYPE temp[] = __VA_ARGS__; \ + Array_##TYPE *arr = malloc(sizeof(Array_##TYPE)); \ + arr->size = sizeof(temp) / sizeof(temp[0]); \ + arr->data = malloc(arr->size * sizeof(TYPE)); \ + memcpy(arr->data, temp, arr->size * sizeof(TYPE)); \ + arr; \ + }) +\end{verbatim} + +\item \texttt{InitArrayWithSize} +\label{sec:orga925ddb} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Location: \texttt{inc/macros.h} +\item Input: a type, a size, and initial value +\item Output: a new \texttt{Array\_type} with the given size filled with the initial value +\end{itemize} + +\begin{verbatim} +#define InitArrayWithSize(TYPE, SIZE, INIT_VALUE) \ + ({ \ + Array_##TYPE *arr = malloc(sizeof(Array_##TYPE)); \ + arr->size = SIZE; \ + arr->data = malloc(arr->size * sizeof(TYPE)); \ + for (size_t i = 0; i < arr->size; i++) \ + arr->data[i] = INIT_VALUE; \ + arr; \ + }) +\end{verbatim} + +\item \texttt{InitMatrixWithSize} +\label{sec:orgf90d7c8} +\begin{itemize} +\item Author: Elizabeth Hunt +\item Location: \texttt{inc/macros.h} +\item Input: a type, number of rows, columns, and initial value +\item Output: a new \texttt{Matrix\_type} of size \texttt{rows x columns} filled with the initial +value +\end{itemize} + +\begin{verbatim} +#define InitMatrixWithSize(TYPE, ROWS, COLS, INIT_VALUE) \ + ({ \ + Matrix_##TYPE *matrix = malloc(sizeof(Matrix_##TYPE)); \ + matrix->rows = ROWS; \ + matrix->cols = COLS; \ + matrix->data = malloc(matrix->rows * sizeof(Array_##TYPE *)); \ + for (size_t y = 0; y < matrix->rows; y++) \ + matrix->data[y] = InitArrayWithSize(TYPE, COLS, INIT_VALUE); \ + matrix; \ + }) +\end{verbatim} +\end{enumerate} +\end{document}
\ No newline at end of file diff --git a/Homework/math4610/homeworks/a.out b/Homework/math4610/homeworks/a.out Binary files differnew file mode 100644 index 0000000..410a7d5 --- /dev/null +++ b/Homework/math4610/homeworks/a.out diff --git a/Homework/math4610/homeworks/hw-2.org b/Homework/math4610/homeworks/hw-2.org new file mode 100644 index 0000000..f696ffb --- /dev/null +++ b/Homework/math4610/homeworks/hw-2.org @@ -0,0 +1,182 @@ +#+TITLE: HW 02 +#+AUTHOR: Elizabeth Hunt +#+STARTUP: entitiespretty fold inlineimages +#+LATEX_HEADER: \notindent \notag \usepackage{amsmath} \usepackage[a4paper,margin=1in,portrait]{geometry} +#+LATEX: \setlength\parindent{0pt} +#+OPTIONS: toc:nil + +* Question One +Computing $\epsilon_{\text{mac}}$ for single precision numbers + +#+BEGIN_SRC lisp :session t :results table + (load "../lizfcm.asd") + (ql:quickload :lizfcm) + + (let ((domain-values (lizfcm.approx:compute-maceps (lambda (x) x) + 1.0 + 1.0))) + (lizfcm.utils:table (:headers '("a" "h" "err") + :domain-order (a h err) + :domain-values domain-values))) +#+END_SRC + +(with many rows truncated) + +| a | h | err | +| 1.0 | 1.0 | 1.0 | +| 1.0 | 0.5 | 0.5 | +| 1.0 | 0.25 | 0.25 | +| 1.0 | 0.125 | 0.125 | +| 1.0 | 0.0625 | 0.0625 | +| 1.0 | 0.03125 | 0.03125 | +| 1.0 | 1.9073486e-06 | 1.9073486e-06 | +| 1.0 | 9.536743e-07 | 9.536743e-07 | +| 1.0 | 4.7683716e-07 | 4.7683716e-07 | +| 1.0 | 2.3841858e-07 | 2.3841858e-07 | +| 1.0 | 1.1920929e-07 | 1.1920929e-07 | + +$\epsilon_{\text{mac single precision}}$ \approx 1.192(10^-7) + +* Question Two +Computing $\epsilon_{\text{mac}}$ for double precision numbers: + +#+BEGIN_SRC lisp :session t :results table + (let ((domain-values (lizfcm.approx:compute-maceps (lambda (x) x) + 1.0d0 + 1.0d0))) + (lizfcm.utils:table (:headers '("a" "h" "err") + :domain-order (a h err) + :domain-values domain-values))) +#+END_SRC + +(with many rows truncated) +| a | h | err | +| 1.0d0 | 1.0d0 | 1.0d0 | +| 1.0d0 | 0.5d0 | 0.5d0 | +| 1.0d0 | 0.25d0 | 0.25d0 | +| 1.0d0 | 0.125d0 | 0.125d0 | +| 1.0d0 | 0.0625d0 | 0.0625d0 | +| 1.0d0 | 0.03125d0 | 0.03125d0 | +| 1.0d0 | 0.015625d0 | 0.015625d0 | +| 1.0d0 | 0.0078125d0 | 0.0078125d0 | +| 1.0d0 | 0.00390625d0 | 0.00390625d0 | +| 1.0d0 | 0.001953125d0 | 0.001953125d0 | +| 1.0d0 | 7.105427357601002d-15 | 7.105427357601002d-15 | +| 1.0d0 | 3.552713678800501d-15 | 3.552713678800501d-15 | +| 1.0d0 | 1.7763568394002505d-15 | 1.7763568394002505d-15 | +| 1.0d0 | 8.881784197001252d-16 | 8.881784197001252d-16 | +| 1.0d0 | 4.440892098500626d-16 | 4.440892098500626d-16 | +| 1.0d0 | 2.220446049250313d-16 | 2.220446049250313d-16 | + +Thus, $\epsilon_{\text{mac double precision}}$ \approx 2.220 \cdot 10^{-16} + +* Question Three - |v|_2 +#+BEGIN_SRC lisp :session t + (let ((vs '((1 1) (2 3) (4 5) (-1 2))) + (2-norm (lizfcm.vector:p-norm 2))) + (lizfcm.utils:table (:headers '("x" "y" "2norm") + :domain-order (x y) + :domain-values vs) + (funcall 2-norm (list x y)))) +#+END_SRC + + +| x | y | 2norm | +| 1 | 1 | 1.4142135 | +| 2 | 3 | 3.6055512 | +| 4 | 5 | 6.4031243 | +| -1 | 2 | 2.236068 | + +* Question Four - |v|_1 +#+BEGIN_SRC lisp :session t + (let ((vs '((1 1) (2 3) (4 5) (-1 2))) + (1-norm (lizfcm.vector:p-norm 1))) + (lizfcm.utils:table (:headers '("x" "y" "1norm") + :domain-order (x y) + :domain-values vs) + (funcall 1-norm (list x y)))) +#+END_SRC + + +| x | y | 1norm | +| 1 | 1 | 2 | +| 2 | 3 | 5 | +| 4 | 5 | 9 | +| -1 | 2 | 3 | + +* Question Five - |v|_{\infty} +#+BEGIN_SRC lisp :session t + (let ((vs '((1 1) (2 3) (4 5) (-1 2)))) + (lizfcm.utils:table (:headers '("x" "y" "max-norm") + :domain-order (x y) + :domain-values vs) + (lizfcm.vector:max-norm (list x y)))) +#+END_SRC + + +| x | y | infty-norm | +| 1 | 1 | 1 | +| 2 | 3 | 3 | +| 4 | 5 | 5 | +| -1 | 2 | 2 | + +* Question Six - ||v - u|| via |v|_{2} +#+BEGIN_SRC lisp :session t + (let ((vs '((1 1) (2 3) (4 5) (-1 2))) + (vs2 '((7 9) (2 2) (8 -1) (4 4))) + (2-norm (lizfcm.vector:p-norm 2))) + (lizfcm.utils:table (:headers '("v1" "v2" "2-norm-d") + :domain-order (v1 v2) + :domain-values (mapcar (lambda (v1 v2) + (list v1 v2)) + vs + vs2)) + (lizfcm.vector:distance v1 v2 2-norm))) +#+END_SRC + + +| v1 | v2 | 2-norm | +| (1 1) | (7 9) | 10.0 | +| (2 3) | (2 2) | 1.0 | +| (4 5) | (8 -1) | 7.2111025 | +| (-1 2) | (4 4) | 5.3851647 | + +* Question Seven - ||v - u|| via |v|_{1} +#+BEGIN_SRC lisp :session t + (let ((vs '((1 1) (2 3) (4 5) (-1 2))) + (vs2 '((7 9) (2 2) (8 -1) (4 4))) + (1-norm (lizfcm.vector:p-norm 1))) + (lizfcm.utils:table (:headers '("v1" "v2" "1-norm-d") + :domain-order (v1 v2) + :domain-values (mapcar (lambda (v1 v2) + (list v1 v2)) + vs + vs2)) + (lizfcm.vector:distance v1 v2 1-norm))) +#+END_SRC + + +| v1 | v2 | 1-norm-d | +| (1 1) | (7 9) | 14 | +| (2 3) | (2 2) | 1 | +| (4 5) | (8 -1) | 10 | +| (-1 2) | (4 4) | 7 | + +* Question Eight - ||v - u|| via |v|_{\infty} +#+BEGIN_SRC lisp :session t + (let ((vs '((1 1) (2 3) (4 5) (-1 2))) + (vs2 '((7 9) (2 2) (8 -1) (4 4)))) + (lizfcm.utils:table (:headers '("v1" "v2" "max-norm-d") + :domain-order (v1 v2) + :domain-values (mapcar (lambda (v1 v2) + (list v1 v2)) + vs + vs2)) + (lizfcm.vector:distance v1 v2 'lizfcm.vector:max-norm))) +#+END_SRC + +| v1 | v2 | max-norm-d | +| (1 1) | (7 9) | -6 | +| (2 3) | (2 2) | 1 | +| (4 5) | (8 -1) | 6 | +| (-1 2) | (4 4) | -2 | diff --git a/Homework/math4610/homeworks/hw-2.pdf b/Homework/math4610/homeworks/hw-2.pdf Binary files differnew file mode 100644 index 0000000..2dc4d28 --- /dev/null +++ b/Homework/math4610/homeworks/hw-2.pdf diff --git a/Homework/math4610/homeworks/hw-2.tex b/Homework/math4610/homeworks/hw-2.tex new file mode 100644 index 0000000..da8d3f5 --- /dev/null +++ b/Homework/math4610/homeworks/hw-2.tex @@ -0,0 +1,246 @@ +% Created 2023-10-07 Sat 14:51 +% Intended LaTeX compiler: pdflatex +\documentclass[11pt]{article} +\usepackage[utf8]{inputenc} +\usepackage[T1]{fontenc} +\usepackage{graphicx} +\usepackage{longtable} +\usepackage{wrapfig} +\usepackage{rotating} +\usepackage[normalem]{ulem} +\usepackage{amsmath} +\usepackage{amssymb} +\usepackage{capt-of} +\usepackage{hyperref} +\notindent \notag \usepackage{amsmath} \usepackage[a4paper,margin=1in,portrait]{geometry} +\author{Elizabeth Hunt} +\date{\today} +\title{HW 02} +\hypersetup{ + pdfauthor={Elizabeth Hunt}, + pdftitle={HW 02}, + pdfkeywords={}, + pdfsubject={}, + pdfcreator={Emacs 28.2 (Org mode 9.7-pre)}, + pdflang={English}} +\begin{document} + +\maketitle +\setlength\parindent{0pt} + +\section{Question One} +\label{sec:org58b9af4} +Computing \(\epsilon_{\text{mac}}\) for single precision numbers + +\begin{verbatim} +(load "../lizfcm.asd") +(ql:quickload :lizfcm) + +(let ((domain-values (lizfcm.approx:compute-maceps (lambda (x) x) + 1.0 + 1.0))) + (lizfcm.utils:table (:headers '("a" "h" "err") + :domain-order (a h err) + :domain-values domain-values))) +\end{verbatim} + +(with many rows truncated) + +\begin{center} +\begin{tabular}{rrr} +a & h & err\\[0pt] +1.0 & 1.0 & 1.0\\[0pt] +1.0 & 0.5 & 0.5\\[0pt] +1.0 & 0.25 & 0.25\\[0pt] +1.0 & 0.125 & 0.125\\[0pt] +1.0 & 0.0625 & 0.0625\\[0pt] +1.0 & 0.03125 & 0.03125\\[0pt] +1.0 & 1.9073486e-06 & 1.9073486e-06\\[0pt] +1.0 & 9.536743e-07 & 9.536743e-07\\[0pt] +1.0 & 4.7683716e-07 & 4.7683716e-07\\[0pt] +1.0 & 2.3841858e-07 & 2.3841858e-07\\[0pt] +1.0 & 1.1920929e-07 & 1.1920929e-07\\[0pt] +\end{tabular} +\end{center} + +\(\epsilon_{\text{mac single precision}}\) \(\approx\) 1.192(10\textsuperscript{-7}) + +\section{Question Two} +\label{sec:org27557b4} +Computing \(\epsilon_{\text{mac}}\) for double precision numbers: + +\begin{verbatim} +(let ((domain-values (lizfcm.approx:compute-maceps (lambda (x) x) + 1.0d0 + 1.0d0))) + (lizfcm.utils:table (:headers '("a" "h" "err") + :domain-order (a h err) + :domain-values domain-values))) +\end{verbatim} + +(with many rows truncated) +\begin{center} +\begin{tabular}{rrr} +a & h & err\\[0pt] +1.0d0 & 1.0d0 & 1.0d0\\[0pt] +1.0d0 & 0.5d0 & 0.5d0\\[0pt] +1.0d0 & 0.25d0 & 0.25d0\\[0pt] +1.0d0 & 0.125d0 & 0.125d0\\[0pt] +1.0d0 & 0.0625d0 & 0.0625d0\\[0pt] +1.0d0 & 0.03125d0 & 0.03125d0\\[0pt] +1.0d0 & 0.015625d0 & 0.015625d0\\[0pt] +1.0d0 & 0.0078125d0 & 0.0078125d0\\[0pt] +1.0d0 & 0.00390625d0 & 0.00390625d0\\[0pt] +1.0d0 & 0.001953125d0 & 0.001953125d0\\[0pt] +1.0d0 & 7.105427357601002d-15 & 7.105427357601002d-15\\[0pt] +1.0d0 & 3.552713678800501d-15 & 3.552713678800501d-15\\[0pt] +1.0d0 & 1.7763568394002505d-15 & 1.7763568394002505d-15\\[0pt] +1.0d0 & 8.881784197001252d-16 & 8.881784197001252d-16\\[0pt] +1.0d0 & 4.440892098500626d-16 & 4.440892098500626d-16\\[0pt] +1.0d0 & 2.220446049250313d-16 & 2.220446049250313d-16\\[0pt] +\end{tabular} +\end{center} + +Thus, \(\epsilon_{\text{mac double precision}}\) \(\approx\) 2.220 \(\cdot\) 10\textsuperscript{-16} + +\section{Question Three - |v|\textsubscript{2}} +\label{sec:org59c6c10} +\begin{verbatim} +(let ((vs '((1 1) (2 3) (4 5) (-1 2))) + (2-norm (lizfcm.vector:p-norm 2))) + (lizfcm.utils:table (:headers '("x" "y" "2norm") + :domain-order (x y) + :domain-values vs) + (funcall 2-norm (list x y)))) +\end{verbatim} + + +\begin{center} +\begin{tabular}{rrr} +x & y & 2norm\\[0pt] +1 & 1 & 1.4142135\\[0pt] +2 & 3 & 3.6055512\\[0pt] +4 & 5 & 6.4031243\\[0pt] +-1 & 2 & 2.236068\\[0pt] +\end{tabular} +\end{center} + +\section{Question Four - |v|\textsubscript{1}} +\label{sec:org2b67b3e} +\begin{verbatim} +(let ((vs '((1 1) (2 3) (4 5) (-1 2))) + (1-norm (lizfcm.vector:p-norm 1))) + (lizfcm.utils:table (:headers '("x" "y" "1norm") + :domain-order (x y) + :domain-values vs) + (funcall 1-norm (list x y)))) +\end{verbatim} + + +\begin{center} +\begin{tabular}{rrr} +x & y & 1norm\\[0pt] +1 & 1 & 2\\[0pt] +2 & 3 & 5\\[0pt] +4 & 5 & 9\\[0pt] +-1 & 2 & 3\\[0pt] +\end{tabular} +\end{center} + +\section{Question Five - |v|\textsubscript{\(\infty\)}} +\label{sec:org922206e} +\begin{verbatim} +(let ((vs '((1 1) (2 3) (4 5) (-1 2)))) + (lizfcm.utils:table (:headers '("x" "y" "max-norm") + :domain-order (x y) + :domain-values vs) + (lizfcm.vector:max-norm (list x y)))) +\end{verbatim} + + +\begin{center} +\begin{tabular}{rrr} +x & y & infty-norm\\[0pt] +1 & 1 & 1\\[0pt] +2 & 3 & 3\\[0pt] +4 & 5 & 5\\[0pt] +-1 & 2 & 2\\[0pt] +\end{tabular} +\end{center} + +\section{Question Six - ||v - u|| via |v|\textsubscript{2}} +\label{sec:org29ec18f} +\begin{verbatim} +(let ((vs '((1 1) (2 3) (4 5) (-1 2))) + (vs2 '((7 9) (2 2) (8 -1) (4 4))) + (2-norm (lizfcm.vector:p-norm 2))) + (lizfcm.utils:table (:headers '("v1" "v2" "2-norm-d") + :domain-order (v1 v2) + :domain-values (mapcar (lambda (v1 v2) + (list v1 v2)) + vs + vs2)) + (lizfcm.vector:distance v1 v2 2-norm))) +\end{verbatim} + + +\begin{center} +\begin{tabular}{llr} +v1 & v2 & 2-norm\\[0pt] +(1 1) & (7 9) & 10.0\\[0pt] +(2 3) & (2 2) & 1.0\\[0pt] +(4 5) & (8 -1) & 7.2111025\\[0pt] +(-1 2) & (4 4) & 5.3851647\\[0pt] +\end{tabular} +\end{center} + +\section{Question Seven - ||v - u|| via |v|\textsubscript{1}} +\label{sec:org7a87810} +\begin{verbatim} +(let ((vs '((1 1) (2 3) (4 5) (-1 2))) + (vs2 '((7 9) (2 2) (8 -1) (4 4))) + (1-norm (lizfcm.vector:p-norm 1))) + (lizfcm.utils:table (:headers '("v1" "v2" "1-norm-d") + :domain-order (v1 v2) + :domain-values (mapcar (lambda (v1 v2) + (list v1 v2)) + vs + vs2)) + (lizfcm.vector:distance v1 v2 1-norm))) +\end{verbatim} + + +\begin{center} +\begin{tabular}{llr} +v1 & v2 & 1-norm-d\\[0pt] +(1 1) & (7 9) & 14\\[0pt] +(2 3) & (2 2) & 1\\[0pt] +(4 5) & (8 -1) & 10\\[0pt] +(-1 2) & (4 4) & 7\\[0pt] +\end{tabular} +\end{center} + +\section{Question Eight - ||v - u|| via |v|\textsubscript{\(\infty\)}} +\label{sec:org0f3b64f} +\begin{verbatim} +(let ((vs '((1 1) (2 3) (4 5) (-1 2))) + (vs2 '((7 9) (2 2) (8 -1) (4 4)))) + (lizfcm.utils:table (:headers '("v1" "v2" "max-norm-d") + :domain-order (v1 v2) + :domain-values (mapcar (lambda (v1 v2) + (list v1 v2)) + vs + vs2)) + (lizfcm.vector:distance v1 v2 'lizfcm.vector:max-norm))) +\end{verbatim} + +\begin{center} +\begin{tabular}{llr} +v1 & v2 & max-norm-d\\[0pt] +(1 1) & (7 9) & -6\\[0pt] +(2 3) & (2 2) & 1\\[0pt] +(4 5) & (8 -1) & 6\\[0pt] +(-1 2) & (4 4) & -2\\[0pt] +\end{tabular} +\end{center} +\end{document}
\ No newline at end of file diff --git a/Homework/math4610/homeworks/hw-3.org b/Homework/math4610/homeworks/hw-3.org new file mode 100644 index 0000000..8d7edcc --- /dev/null +++ b/Homework/math4610/homeworks/hw-3.org @@ -0,0 +1,242 @@ +#+TITLE: HW 03 +#+AUTHOR: Elizabeth Hunt +#+STARTUP: entitiespretty fold inlineimages +#+LATEX_HEADER: \notindent \notag \usepackage{amsmath} \usepackage[a4paper,margin=1in,portrait]{geometry} +#+LATEX: \setlength\parindent{0pt} +#+OPTIONS: toc:nil + +* Question One +** Three Terms +\begin{align*} +Si_3(x) &= \int_0^x \frac{s - \frac{s^3}{3!} + \frac{s^5}{5!}}{s} dx \\ +&= x - \frac{x^3}{(3!)(3)} + \frac{x^5}{(5!)(5)} +\end{align*} +** Five Terms +\begin{align*} +Si_3(x) &= \int_0^x \frac{s - \frac{s^3}{3!} + \frac{s^5}{5!} - \frac{s^7}{7!} + \frac{s^9}{9!}}{s} dx \\ +&= x - \frac{x^3}{(3!)(3)} + \frac{x^5}{(5!)(5)} - \frac{x^7}{(7!)(7)} + \frac{s^9}{(9!)(9)} +\end{align*} +** Ten Terms +\begin{align*} +Si_{10}(x) &= \int_0^x \frac{s - \frac{s^3}{3!} + \frac{s^5}{5!} - \frac{s^7}{7!} + \frac{s^9}{9!} - \frac{s^{11}}{11!} + \frac{s^{13}}{13!} - \frac{s^{15}}{15!} + \frac{s^{17}}{17!} - \frac{s^{19}}{19!}}{s} ds \\ +&= x - \frac{x^3}{(3!)(3)} + \frac{x^5}{(5!)(5)} - \frac{x^7}{(7!)(7)} + \frac{s^9}{(9!)(9)} - \frac{s^{11}}{(11!)(11)} + \frac{s^{13}}{(13!)(13)} - \frac{s^{15}}{(15!)(15)} \\ +&+ \frac{s^{17}}{(17!)(17)} - \frac{s^{19}}{(19!)(19)} +\end{align*} +* Question Three +For the second term in the difference quotient, we can expand the taylor series centered at x=a: + +\begin{equation*} +f(x) = f(a) + f'(a)(x-a) + \frac{f''(a)}{2}(x-a)^2 + \cdots \\ +\end{equation*} + +Which we substitute into the difference quotient: + +\begin{equation*} +\frac{f(a) - f(a - h)}{h} = \frac{f(a) - (f(a) + f'(a)(x-a) + \frac{f''(a)}{2}(x-a)^2 + \cdots)}{h} +\end{equation*} + +And subs. $x=a-h$: + +\begin{align*} +\frac{f(a) - (f(a) + f'(a)(x-a) + \frac{f''(a)}{2}(x-a)^2 + \cdots)}{h} &= -f'(a)(-1) + -\frac{1}{2}f''(a)h \\ +&= f'(a) - \frac{1}{2}f''(a)h + \cdots \\ +\end{align*} + +Which we now plug into the initial $e_{\text{abs}}$: + +\begin{align*} +e_{\text{abs}} &= |f'(a) - \frac{f(a) - f(a - h)}{h}| \\ +&= |f'(a) - (f'(a) + -\frac{f''(a)}{2}h + \cdots)| \\ +&= |- \frac{1}{2}f''(a)h + \cdots | \\ +\end{align*} + +With the Taylor Remainder theorem we can absorb the series following the second term: + +\begin{equation*} +e_{\text{abs}} = |- \frac{1}{2}f''(a)h + \cdots | = |\frac{1}{2}f''(\xi)h| \leq Ch +\end{equation*} + +Thus our error is bounded linearly with $h$. + +* Question Four +For the first term in the difference quotient we know, from the given notes, + +\begin{equation*} +f(a+h) = f(a) + f'(a)h + \frac{1}{2}f''(a)h^2 + \frac{1}{6}f'''(a)(h^3) +\end{equation*} + +And from some of the work in Question Three, + +\begin{equation*} +f(a - h) = f(a) + f'(a)(-h) + \frac{1}{2}f''(a)(-h)^2 + \frac{1}{6}f'''(a)(-h^3) +\end{equation*} + +We can substitute immediately into $e_{\text{abs}} = |f'(a) - (\frac{f(a+h) - f(a-h)}{2h})|$: + +\begin{align*} +e_{\text{abs}} &= |f'(a) - \frac{1}{2h}((f(a) + f'(a)h + \frac{1}{2}f''(a)h^2 + \cdots) - (f(a) - f'(a)h + \frac{1}{2}f''(a)h^2 + \cdots))| \\ +&= |f'(a) - \frac{1}{2h}(2f'(a)h + \frac{1}{6}f'''(a)h^3 + \cdots)| \\ +&= |f'(a) - f'(a) - \frac{1}{12}f'''(a)h^2 + \cdots| \\ +&= |-\frac{1}{12}f'''(a)h^2 + \cdots| +\end{align*} + +Finally, with the Taylor Remainder theorem we can absorb the series following the third term: + +\begin{equation*} +e_{\text{abs}} = |-\frac{1}{12}f'''(\xi)h^2| = |\frac{1}{12}f'''(\xi)h^2| \leq Ch^2 +\end{equation*} + +Meaning that as $h$ scales linearly, our error is bounded by $h^2$ as opposed to linearly as in Question Three. + +* Question Six +** A +#+BEGIN_SRC lisp + (load "../lizfcm.asd") + (ql:quickload :lizfcm) + + (defun f (x) + (/ (- x 1) (+ x 1))) + + (defun fprime (x) + (/ 2 (expt (+ x 1) 2))) + + (let ((domain-values (loop for a from 0 to 2 + append + (loop for i from 0 to 9 + for h = (/ 1.0 (expt 2 i)) + collect (list a h))))) + (lizfcm.utils:table (:headers '("a" "h" "f'" "\\approx f'" "e_{\\text{abs}}") + :domain-order (a h) + :domain-values domain-values) + (fprime a) + (lizfcm.approx:fwd-derivative-at 'f a h) + (abs (- (fprime a) + (lizfcm.approx:fwd-derivative-at 'f a h))))) +#+END_SRC + +#+RESULTS: +| a | h | f' | \approx f' | e_{\text{abs}} | +| 0 | 1.0 | 2 | 1.0 | 1.0 | +| 0 | 0.5 | 2 | 1.3333333 | 0.66666675 | +| 0 | 0.25 | 2 | 1.5999999 | 0.4000001 | +| 0 | 0.125 | 2 | 1.7777777 | 0.22222233 | +| 0 | 0.0625 | 2 | 1.8823528 | 0.11764717 | +| 0 | 0.03125 | 2 | 1.939394 | 0.060606003 | +| 0 | 0.015625 | 2 | 1.9692307 | 0.030769348 | +| 0 | 0.0078125 | 2 | 1.9844971 | 0.01550293 | +| 0 | 0.00390625 | 2 | 1.992218 | 0.0077819824 | +| 0 | 0.001953125 | 2 | 1.9960938 | 0.00390625 | +| 1 | 1.0 | 1/2 | 0.33333334 | 0.16666666 | +| 1 | 0.5 | 1/2 | 0.4 | 0.099999994 | +| 1 | 0.25 | 1/2 | 0.44444445 | 0.055555552 | +| 1 | 0.125 | 1/2 | 0.47058824 | 0.029411763 | +| 1 | 0.0625 | 1/2 | 0.4848485 | 0.015151501 | +| 1 | 0.03125 | 1/2 | 0.4923077 | 0.0076923072 | +| 1 | 0.015625 | 1/2 | 0.49612403 | 0.0038759708 | +| 1 | 0.0078125 | 1/2 | 0.49805447 | 0.0019455254 | +| 1 | 0.00390625 | 1/2 | 0.49902534 | 0.00097465515 | +| 1 | 0.001953125 | 1/2 | 0.4995122 | 0.0004878044 | +| 2 | 1.0 | 2/9 | 0.16666666 | 0.055555567 | +| 2 | 0.5 | 2/9 | 0.19047618 | 0.031746045 | +| 2 | 0.25 | 2/9 | 0.2051282 | 0.017094031 | +| 2 | 0.125 | 2/9 | 0.21333337 | 0.008888856 | +| 2 | 0.0625 | 2/9 | 0.21768713 | 0.004535094 | +| 2 | 0.03125 | 2/9 | 0.21993065 | 0.002291575 | +| 2 | 0.015625 | 2/9 | 0.22106934 | 0.0011528879 | +| 2 | 0.0078125 | 2/9 | 0.22164536 | 0.00057686865 | +| 2 | 0.00390625 | 2/9 | 0.22193146 | 0.00029076636 | +| 2 | 0.001953125 | 2/9 | 0.22207642 | 0.00014580786 | + +* Question Nine +** C + +#+BEGIN_SRC lisp + (load "../lizfcm.asd") + (ql:quickload :lizfcm) + + (defun factorial (n) + (if (= n 0) + 1 + (* n (factorial (- n 1))))) + + (defun taylor-term (n x) + (/ (* (expt (- 1) n) + (expt x (+ (* 2 n) 1))) + (* (factorial n) + (+ (* 2 n) 1)))) + + (defun f (x &optional (max-iterations 30)) + (let ((sum 0.0)) + (dotimes (n max-iterations) + (setq sum (+ sum (taylor-term n x)))) + (* sum (/ 2 (sqrt pi))))) + + (defun fprime (x) + (* (/ 2 (sqrt pi)) (exp (- 0 (* x x))))) + + (let ((domain-values (loop for a from 0 to 1 + append + (loop for i from 0 to 9 + for h = (/ 1.0 (expt 2 i)) + collect (list a h))))) + (lizfcm.utils:table (:headers '("a" "h" "f'" "\\approx f'" "e_{\\text{abs}}") + :domain-order (a h) + :domain-values domain-values) + (fprime a) + (lizfcm.approx:central-derivative-at 'f a h) + (abs (- (fprime a) + (lizfcm.approx:central-derivative-at 'f a h))))) +#+END_SRC + + +| a | h | f' | \approx f' | e_{\text{abs}} | +| 0 | 1.0 | 1.1283791670955126d0 | 0.8427006725464232d0 | 0.28567849454908933d0 | +| 0 | 0.5 | 1.1283791670955126d0 | 1.0409997446922075d0 | 0.0873794224033051d0 | +| 0 | 0.25 | 1.1283791670955126d0 | 1.1053055663206806d0 | 0.023073600774832004d0 | +| 0 | 0.125 | 1.1283791670955126d0 | 1.122529655394656d0 | 0.005849511700856569d0 | +| 0 | 0.0625 | 1.1283791670955126d0 | 1.1269116944798618d0 | 0.0014674726156507223d0 | +| 0 | 0.03125 | 1.1283791670955126d0 | 1.1280120131008824d0 | 3.6715399463016496d-4 | +| 0 | 0.015625 | 1.1283791670955126d0 | 1.1282873617826952d0 | 9.180531281738347d-5 | +| 0 | 0.0078125 | 1.1283791670955126d0 | 1.128356232581468d0 | 2.293451404455915d-5 | +| 0 | 0.00390625 | 1.1283791670955126d0 | 1.1283734502811613d0 | 5.71681435124205d-6 | +| 0 | 0.001953125 | 1.1283791670955126d0 | 1.1283777547060847d0 | 1.4123894278572635d-6 | +| 1 | 1.0 | 0.41510750774498784d0 | 0.4976611317561498d0 | 0.08255362401116195d0 | +| 1 | 0.5 | 0.41510750774498784d0 | 0.44560523266293384d0 | 0.030497724917946d0 | +| 1 | 0.25 | 0.41510750774498784d0 | 0.4234889628937013d0 | 0.008381455148713468d0 | +| 1 | 0.125 | 0.41510750774498784d0 | 0.41725265825950153d0 | 0.002145150514513694d0 | +| 1 | 0.0625 | 0.41510750774498784d0 | 0.41564710776310854d0 | 5.396000181207006d-4 | +| 1 | 0.03125 | 0.41510750774498784d0 | 0.4152414157140871d0 | 1.3390796909928948d-4 | +| 1 | 0.015625 | 0.41510750774498784d0 | 0.41514241394084905d0 | 3.490619586121735d-5 | +| 1 | 0.0078125 | 0.41510750774498784d0 | 0.41510582632900395d0 | 1.6814159838896003d-6 | +| 1 | 0.00390625 | 0.41510750774498784d0 | 0.415092913054238d0 | 1.4594690749825112d-5 | +| 1 | 0.001953125 | 0.41510750774498784d0 | 0.4150670865046777d0 | 4.0421240310117845d-5 | + +* Question Twelve + +First we'll place a bound on $h$; looking at a graph of $f$ it's pretty obvious from the asymptotes that we don't want to go much further than $|h| = 2 - \frac{pi}{2}$. + +Following similar reasoning as Question Four, we can determine an optimal $h$ by computing $e_{\text{abs}}$ for the central difference, but now including a roundoff error for each time we run $f$ +such that $|f_{\text{machine}}(x) - f(x)| \le \epsilon_{\text{dblprec}}$ (we'll use double precision numbers, from HW 2 we know $\epsilon_{\text{dblprec}} \approx 2.22045 (10^{-16})$). + +We'll just assume $|f_{\text{machine}}(x) - f(x)| = \epsilon_{\text{dblprec}}$ so our new difference quotient becomes: + +\begin{align*} +e_{\text{abs}} &= |f'(a) - (\frac{f(a+h) - f(a-h) + 2\epsilon_{\text{dblprec}}}{2h})| \\ +&= |\frac{1}{12}f'''(\xi)h^2 + \frac{\epsilon_{\text{dblprec}}}{h}| +\end{align*} + +Because we bounded our $|h| = 2 - \frac{pi}{2}$ we'll find the maximum value of $f'''$ between $a - (2 - \frac{\pi}{2})$ and $a - (2 - \frac{\pi}{3})$. Using [[https://www.desmos.com/calculator/gen1zpohh2][desmos]] I found this to be -2. + +Thus, $e_{\text{abs}} \leq \frac{1}{6}h^2 + \frac{\epsilon_{\text{dblprec}}}{h}$. Finding the derivative: + +\begin{equation*} +e' = \frac{1}{3}h - \frac{\epsilon_{\text{dblprec}}}{h^2} +\end{equation*} + +And solving at $e' = 0$: + +\begin{equation*} +\frac{1}{3}h = \frac{\epsilon_{\text{dblprec}}}{h^2} \Rightarrow h^3 = 3\epsilon_{\text{dblprec}} \Rightarrow h = (3\epsilon_{\text{dblprec}})^{1/3} +\end{equation*} + +Which is $\approx (3(2.22045 (10^{-16}))^{\frac{1}{3}} \approx 8.7335 10^{-6}$. diff --git a/Homework/math4610/homeworks/hw-3.pdf b/Homework/math4610/homeworks/hw-3.pdf Binary files differnew file mode 100644 index 0000000..1f9bac6 --- /dev/null +++ b/Homework/math4610/homeworks/hw-3.pdf diff --git a/Homework/math4610/homeworks/hw-3.tex b/Homework/math4610/homeworks/hw-3.tex new file mode 100644 index 0000000..b3d029d --- /dev/null +++ b/Homework/math4610/homeworks/hw-3.tex @@ -0,0 +1,250 @@ +% Created 2023-10-07 Sat 14:49 +% Intended LaTeX compiler: pdflatex +\documentclass[11pt]{article} +\usepackage[utf8]{inputenc} +\usepackage[T1]{fontenc} +\usepackage{graphicx} +\usepackage{longtable} +\usepackage{wrapfig} +\usepackage{rotating} +\usepackage[normalem]{ulem} +\usepackage{amsmath} +\usepackage{amssymb} +\usepackage{capt-of} +\usepackage{hyperref} +\notindent \notag \usepackage{amsmath} \usepackage[a4paper,margin=1in,portrait]{geometry} +\author{Elizabeth Hunt} +\date{\today} +\title{HW 03} +\hypersetup{ + pdfauthor={Elizabeth Hunt}, + pdftitle={HW 03}, + pdfkeywords={}, + pdfsubject={}, + pdfcreator={Emacs 28.2 (Org mode 9.7-pre)}, + pdflang={English}} +\begin{document} + +\maketitle +\setlength\parindent{0pt} + +\section{Question One} +\label{sec:org6f2bd27} +\subsection{Three Terms} +\label{sec:orgeb827ff} +\begin{align*} +Si_3(x) &= \int_0^x \frac{s - \frac{s^3}{3!} + \frac{s^5}{5!}}{s} dx \\ +&= x - \frac{x^3}{(3!)(3)} + \frac{x^5}{(5!)(5)} +\end{align*} +\subsection{Five Terms} +\label{sec:orge6a15e4} +\begin{align*} +Si_3(x) &= \int_0^x \frac{s - \frac{s^3}{3!} + \frac{s^5}{5!} - \frac{s^7}{7!} + \frac{s^9}{9!}}{s} dx \\ +&= x - \frac{x^3}{(3!)(3)} + \frac{x^5}{(5!)(5)} - \frac{x^7}{(7!)(7)} + \frac{s^9}{(9!)(9)} +\end{align*} +\subsection{Ten Terms} +\label{sec:orge87e346} +\begin{align*} +Si_{10}(x) &= \int_0^x \frac{s - \frac{s^3}{3!} + \frac{s^5}{5!} - \frac{s^7}{7!} + \frac{s^9}{9!} - \frac{s^{11}}{11!} + \frac{s^{13}}{13!} - \frac{s^{15}}{15!} + \frac{s^{17}}{17!} - \frac{s^{19}}{19!}}{s} ds \\ +&= x - \frac{x^3}{(3!)(3)} + \frac{x^5}{(5!)(5)} - \frac{x^7}{(7!)(7)} + \frac{s^9}{(9!)(9)} - \frac{s^{11}}{(11!)(11)} + \frac{s^{13}}{(13!)(13)} - \frac{s^{15}}{(15!)(15)} \\ +&+ \frac{s^{17}}{(17!)(17)} - \frac{s^{19}}{(19!)(19)} +\end{align*} +\section{Question Three} +\label{sec:org6e2f7fc} +For the second term in the difference quotient, we can expand the taylor series centered at x=a: + +\begin{equation*} +f(x) = f(a) + f'(a)(x-a) + \frac{f''(a)}{2}(x-a)^2 + \cdots \\ +\end{equation*} + +Which we substitute into the difference quotient: + +\begin{equation*} +\frac{f(a) - f(a - h)}{h} = \frac{f(a) - (f(a) + f'(a)(x-a) + \frac{f''(a)}{2}(x-a)^2 + \cdots)}{h} +\end{equation*} + +And subs. \(x=a-h\): + +\begin{align*} +\frac{f(a) - (f(a) + f'(a)(x-a) + \frac{f''(a)}{2}(x-a)^2 + \cdots)}{h} &= -f'(a)(-1) + -\frac{1}{2}f''(a)h \\ +&= f'(a) - \frac{1}{2}f''(a)h + \cdots \\ +\end{align*} + +Which we now plug into the initial \(e_{\text{abs}}\): + +\begin{align*} +e_{\text{abs}} &= |f'(a) - \frac{f(a) - f(a - h)}{h}| \\ +&= |f'(a) - (f'(a) + -\frac{f''(a)}{2}h + \cdots)| \\ +&= |- \frac{1}{2}f''(a)h + \cdots | \\ +\end{align*} + +With the Taylor Remainder theorem we can absorb the series following the second term: + +\begin{equation*} +e_{\text{abs}} = |- \frac{1}{2}f''(a)h + \cdots | = |\frac{1}{2}f''(\xi)h| \leq Ch +\end{equation*} + +Thus our error is bounded linearly with \(h\). + +\section{Question Four} +\label{sec:orga7d02a2} +For the first term in the difference quotient we know, from the given notes, + +\begin{equation*} +f(a+h) = f(a) + f'(a)h + \frac{1}{2}f''(a)h^2 + \frac{1}{6}f'''(a)(h^3) +\end{equation*} + +And from some of the work in Question Three, + +\begin{equation*} +f(a - h) = f(a) + f'(a)(-h) + \frac{1}{2}f''(a)(-h)^2 + \frac{1}{6}f'''(a)(-h^3) +\end{equation*} + +We can substitute immediately into \(e_{\text{abs}} = |f'(a) - (\frac{f(a+h) - f(a-h)}{2h})|\): + +\begin{align*} +e_{\text{abs}} &= |f'(a) - \frac{1}{2h}((f(a) + f'(a)h + \frac{1}{2}f''(a)h^2 + \cdots) - (f(a) - f'(a)h + \frac{1}{2}f''(a)h^2 + \cdots))| \\ +&= |f'(a) - \frac{1}{2h}(2f'(a)h + \frac{1}{6}f'''(a)h^3 + \cdots)| \\ +&= |f'(a) - f'(a) - \frac{1}{12}f'''(a)h^2 + \cdots| \\ +&= |-\frac{1}{12}f'''(a)h^2 + \cdots| +\end{align*} + +Finally, with the Taylor Remainder theorem we can absorb the series following the third term: + +\begin{equation*} +e_{\text{abs}} = |-\frac{1}{12}f'''(\xi)h^2| = |\frac{1}{12}f'''(\xi)h^2| \leq Ch^2 +\end{equation*} + +Meaning that as \(h\) scales linearly, our error is bounded by \(h^2\) as opposed to linearly as in Question Three. + +\section{Question Six} +\label{sec:org7b05811} +\subsection{A} +\label{sec:org8341a77} +\begin{verbatim} +(load "../lizfcm.asd") +(ql:quickload :lizfcm) + +(defun f (x) + (/ (- x 1) (+ x 1))) + +(defun fprime (x) + (/ 2 (expt (+ x 1) 2))) + +(let ((domain-values (loop for a from 0 to 2 + append + (loop for i from 0 to 9 + for h = (/ 1.0 (expt 2 i)) + collect (list a h))))) + (lizfcm.utils:table (:headers '("a" "h" "f'" "\\approx f'" "e_{\\text{abs}}") + :domain-order (a h) + :domain-values domain-values) + (fprime a) + (lizfcm.approx:fwd-derivative-at 'f a h) + (abs (- (fprime a) + (lizfcm.approx:fwd-derivative-at 'f a h))))) +\end{verbatim} + + +\section{Question Nine} +\label{sec:orgeb1839f} +\subsection{C} +\label{sec:org5691277} + +\begin{verbatim} +(load "../lizfcm.asd") +(ql:quickload :lizfcm) + +(defun factorial (n) + (if (= n 0) + 1 + (* n (factorial (- n 1))))) + +(defun taylor-term (n x) + (/ (* (expt (- 1) n) + (expt x (+ (* 2 n) 1))) + (* (factorial n) + (+ (* 2 n) 1)))) + +(defun f (x &optional (max-iterations 30)) + (let ((sum 0.0)) + (dotimes (n max-iterations) + (setq sum (+ sum (taylor-term n x)))) + (* sum (/ 2 (sqrt pi))))) + +(defun fprime (x) + (* (/ 2 (sqrt pi)) (exp (- 0 (* x x))))) + +(let ((domain-values (loop for a from 0 to 1 + append + (loop for i from 0 to 9 + for h = (/ 1.0 (expt 2 i)) + collect (list a h))))) + (lizfcm.utils:table (:headers '("a" "h" "f'" "\\approx f'" "e_{\\text{abs}}") + :domain-order (a h) + :domain-values domain-values) + (fprime a) + (lizfcm.approx:central-derivative-at 'f a h) + (abs (- (fprime a) + (lizfcm.approx:central-derivative-at 'f a h))))) +\end{verbatim} + + +\begin{center} +\begin{tabular}{rrrrr} +a & h & f' & \(\approx\) f' & e\textsubscript{\text{abs}}\\[0pt] +0 & 1.0 & 1.1283791670955126d0 & 0.8427006725464232d0 & 0.28567849454908933d0\\[0pt] +0 & 0.5 & 1.1283791670955126d0 & 1.0409997446922075d0 & 0.0873794224033051d0\\[0pt] +0 & 0.25 & 1.1283791670955126d0 & 1.1053055663206806d0 & 0.023073600774832004d0\\[0pt] +0 & 0.125 & 1.1283791670955126d0 & 1.122529655394656d0 & 0.005849511700856569d0\\[0pt] +0 & 0.0625 & 1.1283791670955126d0 & 1.1269116944798618d0 & 0.0014674726156507223d0\\[0pt] +0 & 0.03125 & 1.1283791670955126d0 & 1.1280120131008824d0 & 3.6715399463016496d-4\\[0pt] +0 & 0.015625 & 1.1283791670955126d0 & 1.1282873617826952d0 & 9.180531281738347d-5\\[0pt] +0 & 0.0078125 & 1.1283791670955126d0 & 1.128356232581468d0 & 2.293451404455915d-5\\[0pt] +0 & 0.00390625 & 1.1283791670955126d0 & 1.1283734502811613d0 & 5.71681435124205d-6\\[0pt] +0 & 0.001953125 & 1.1283791670955126d0 & 1.1283777547060847d0 & 1.4123894278572635d-6\\[0pt] +1 & 1.0 & 0.41510750774498784d0 & 0.4976611317561498d0 & 0.08255362401116195d0\\[0pt] +1 & 0.5 & 0.41510750774498784d0 & 0.44560523266293384d0 & 0.030497724917946d0\\[0pt] +1 & 0.25 & 0.41510750774498784d0 & 0.4234889628937013d0 & 0.008381455148713468d0\\[0pt] +1 & 0.125 & 0.41510750774498784d0 & 0.41725265825950153d0 & 0.002145150514513694d0\\[0pt] +1 & 0.0625 & 0.41510750774498784d0 & 0.41564710776310854d0 & 5.396000181207006d-4\\[0pt] +1 & 0.03125 & 0.41510750774498784d0 & 0.4152414157140871d0 & 1.3390796909928948d-4\\[0pt] +1 & 0.015625 & 0.41510750774498784d0 & 0.41514241394084905d0 & 3.490619586121735d-5\\[0pt] +1 & 0.0078125 & 0.41510750774498784d0 & 0.41510582632900395d0 & 1.6814159838896003d-6\\[0pt] +1 & 0.00390625 & 0.41510750774498784d0 & 0.415092913054238d0 & 1.4594690749825112d-5\\[0pt] +1 & 0.001953125 & 0.41510750774498784d0 & 0.4150670865046777d0 & 4.0421240310117845d-5\\[0pt] +\end{tabular} +\end{center} + + +\section{Question Twelve} +\label{sec:orgc55bfd1} + +First we'll place a bound on \(h\); looking at a graph of \(f\) it's pretty obvious from the asymptotes that we don't want to go much further than \(|h| = 2 - \frac{pi}{2}\). + +Following similar reasoning as Question Four, we can determine an optimal \(h\) by computing \(e_{\text{abs}}\) for the central difference, but now including a roundoff error for each time we run \(f\) +such that \(|f_{\text{machine}}(x) - f(x)| \le \epsilon_{\text{dblprec}}\) (we'll use double precision numbers, from HW 2 we know \(\epsilon_{\text{dblprec}} \approx 2.22045 (10^{-16})\)). + +We'll just assume \(|f_{\text{machine}}(x) - f(x)| = \epsilon_{\text{dblprec}}\) so our new difference quotient becomes: + +\begin{align*} +e_{\text{abs}} &= |f'(a) - (\frac{f(a+h) - f(a-h) + 2\epsilon_{\text{dblprec}}}{2h})| \\ +&= |\frac{1}{12}f'''(\xi)h^2 + \frac{\epsilon_{\text{dblprec}}}{h}| +\end{align*} + +Because we bounded our \(|h| = 2 - \frac{pi}{2}\) we'll find the maximum value of \(f'''\) between \(a - (2 - \frac{\pi}{2})\) and \(a - (2 - \frac{\pi}{3})\). Using \href{https://www.desmos.com/calculator/gen1zpohh2}{desmos} I found this to be -2. + +Thus, \(e_{\text{abs}} \leq \frac{1}{6}h^2 + \frac{\epsilon_{\text{dblprec}}}{h}\). Finding the derivative: + +\begin{equation*} +e' = \frac{1}{3}h - \frac{\epsilon_{\text{dblprec}}}{h^2} +\end{equation*} + +And solving at \(e' = 0\): + +\begin{equation*} +\frac{1}{3}h = \frac{\epsilon_{\text{dblprec}}}{h^2} \Rightarrow h^3 = 3\epsilon_{\text{dblprec}} \Rightarrow h = (3\epsilon_{\text{dblprec}})^{1/3} +\end{equation*} + +Which is \(\approx (3(2.22045 (10^{-16}))^{\frac{1}{3}} \approx 8.7335 10^{-6}\). +\end{document}
\ No newline at end of file diff --git a/Homework/math4610/homeworks/hw-4.org b/Homework/math4610/homeworks/hw-4.org new file mode 100644 index 0000000..8884f63 --- /dev/null +++ b/Homework/math4610/homeworks/hw-4.org @@ -0,0 +1,34 @@ +#+TITLE: Homework 4 +#+AUTHOR: Elizabeth Hunt +#+LATEX_HEADER: \notindent \notag \usepackage{amsmath} \usepackage[a4paper,margin=1in,portrait]{geometry} +#+LATEX: \setlength\parindent{0pt} +#+OPTIONS: toc:nil + +* Question 1 +See the attached LIZFCM Software Manual. + +* Question 2, 3, 4 +#+attr_latex: :width 350px +[[./img/make_run.png]] + +* Question 5 +#+attr_latex: :width 350px +[[./img/test_routine_1.png]] + +#+attr_latex: :width 350px +[[./img/test_routine_2.png]] + +* Question 6 +See the LIZFCM Software Manual. + +* Question 7 +See ~src/matrix.c -> lu_decomp, fsubst, bsubst, solve_matrix~ + +* Question 8 +See ~test/main.c -> lines 109 - 113~ in correspondence to the run in Question 5 + +* Question 9 +See ~test/main.c -> lines 118 - 121~ in correspondence to the run in Question 5 + +* Question 10 +See the TOC on the first page of the LIZFCM Software Manual. diff --git a/Homework/math4610/homeworks/hw-4.pdf b/Homework/math4610/homeworks/hw-4.pdf Binary files differnew file mode 100644 index 0000000..35176a1 --- /dev/null +++ b/Homework/math4610/homeworks/hw-4.pdf diff --git a/Homework/math4610/homeworks/hw-4.tex b/Homework/math4610/homeworks/hw-4.tex new file mode 100644 index 0000000..7a83378 --- /dev/null +++ b/Homework/math4610/homeworks/hw-4.tex @@ -0,0 +1,70 @@ +% Created 2023-10-13 Fri 21:11 +% Intended LaTeX compiler: pdflatex +\documentclass[11pt]{article} +\usepackage[utf8]{inputenc} +\usepackage[T1]{fontenc} +\usepackage{graphicx} +\usepackage{longtable} +\usepackage{wrapfig} +\usepackage{rotating} +\usepackage[normalem]{ulem} +\usepackage{amsmath} +\usepackage{amssymb} +\usepackage{capt-of} +\usepackage{hyperref} +\notindent \notag \usepackage{amsmath} \usepackage[a4paper,margin=1in,portrait]{geometry} +\author{Elizabeth Hunt} +\date{\today} +\title{Homework 4} +\hypersetup{ + pdfauthor={Elizabeth Hunt}, + pdftitle={Homework 4}, + pdfkeywords={}, + pdfsubject={}, + pdfcreator={Emacs 28.2 (Org mode 9.7-pre)}, + pdflang={English}} +\begin{document} + +\maketitle +\setlength\parindent{0pt} + +\section{Question 1} +\label{sec:orgf7348d4} +See the attached LIZFCM Software Manual. + +\section{Question 2, 3, 4} +\label{sec:orgaf52510} +\begin{center} +\includegraphics[width=350px]{./img/make_run.png} +\end{center} + +\section{Question 5} +\label{sec:orgd0fe6e8} +\begin{center} +\includegraphics[width=350px]{./img/test_routine_1.png} +\end{center} + +\begin{center} +\includegraphics[width=350px]{./img/test_routine_2.png} +\end{center} + +\section{Question 6} +\label{sec:org9e2023c} +See the LIZFCM Software Manual. + +\section{Question 7} +\label{sec:org6c11571} +See \texttt{src/matrix.c -> lu\_decomp, fsubst, bsubst, solve\_matrix} + +\section{Question 8} +\label{sec:org9ba7792} +See \texttt{test/main.c -> lines 109 - 113} in correspondence to the run in Question 5 + +\section{Question 9} +\label{sec:org3cff888} +See \texttt{test/main.c -> lines 118 - 121} in correspondence to the run in Question 5 + +\section{Question 10} +\label{sec:org522eabc} +See the TOC on the first page of the LIZFCM Software Manual. +\end{document}
\ No newline at end of file diff --git a/Homework/math4610/homeworks/hw-5.org b/Homework/math4610/homeworks/hw-5.org new file mode 100644 index 0000000..a2339f9 --- /dev/null +++ b/Homework/math4610/homeworks/hw-5.org @@ -0,0 +1,59 @@ +#+TITLE: Homework 5 +#+AUTHOR: Elizabeth Hunt +#+LATEX_HEADER: \notindent \notag \usepackage{amsmath} \usepackage[a4paper,margin=1in,portrait]{geometry} +#+LATEX: \setlength\parindent{0pt} +#+OPTIONS: toc:nil + +* Question One +See LIZFCM \rightarrow Matrix Routines \rightarrow ~lu decomp~ & ~bsubst~. + +The test ~UTEST(matrix, lu_decomp)~ is a unit test for the ~lu_decomp~ routine, +and ~UTEST(matrix, bsubst)~ verifies back substitution on an upper triangular +3 \times 3 matrix with a known solution that can be verified manually. + +Both can be found in ~tests/matrix.t.c~. + +* Question Two +Unless the following are met, the resulting solution will be garbage. + +1. The matrix $U$ must be not be singular. +2. $U$ must be square (or it will fail the ~assert~). +3. The system created by $Ux = b$ must be consistent. +4. $U$ is (quite obviously) in upper-triangular form. + +Thus, the actual calculation performing the $LU$ decomposition +(in ~lu_decomp~) does a sanity +check for 1-3 will fail an assert, should a point along the diagonal (pivot) be +zero, or the matrix be non-factorable. + +* Question Three +See LIZFCM \rightarrow Matrix Routines \rightarrow ~fsubst~. + +~UTEST(matrix, fsubst)~ verifies forward substitution on a lower triangular 3 \times 3 +matrix with a known solution that can be verified manually. + +* Question Four + +See LIZFCM \rightarrow Matrix Routines \rightarrow ~gaussian_elimination~ and ~solve_gaussian_elimination~. + +* Question Five +See LIZFCM \rightarrow Matrix Routines \rightarrow ~m_dot_v~, and the ~UTEST(matrix, m_dot_v)~ in +~tests/matrix.t.c~. + +* Question Six +See ~UTEST(matrix, solve_gaussian_elimination)~ in ~tests/matrix.t.c~, which generates a diagonally dominant 10 \times 10 matrix +and shows that the solution is consistent with the initial matrix, according to the steps given. Then, +we do a dot product between each row of the diagonally dominant matrix and the solution vector to ensure +it is near equivalent to the input vector. + +* Question Seven +See ~UTEST(matrix, solve_matrix_lu_bsubst)~ which does the same test in Question Six with the solution according to +~solve_matrix_lu_bsubst~ as shown in the Software Manual. + +* Question Eight +No, since the time complexity for Gaussian Elimination is always less than that of the LU factorization solution by $O(n^2)$ operations +(in LU factorization we perform both backwards and forwards substitutions proceeding the LU decomp, in Gaussian Elimination we only need +back substitution). + +* Question Nine, Ten +See LIZFCM Software manual and shared library in ~dist~ after compiling. diff --git a/Homework/math4610/homeworks/hw-5.pdf b/Homework/math4610/homeworks/hw-5.pdf Binary files differnew file mode 100644 index 0000000..a7773bc --- /dev/null +++ b/Homework/math4610/homeworks/hw-5.pdf diff --git a/Homework/math4610/homeworks/hw-5.tex b/Homework/math4610/homeworks/hw-5.tex new file mode 100644 index 0000000..98cca2e --- /dev/null +++ b/Homework/math4610/homeworks/hw-5.tex @@ -0,0 +1,95 @@ +% Created 2023-11-01 Wed 20:49 +% Intended LaTeX compiler: pdflatex +\documentclass[11pt]{article} +\usepackage[utf8]{inputenc} +\usepackage[T1]{fontenc} +\usepackage{graphicx} +\usepackage{longtable} +\usepackage{wrapfig} +\usepackage{rotating} +\usepackage[normalem]{ulem} +\usepackage{amsmath} +\usepackage{amssymb} +\usepackage{capt-of} +\usepackage{hyperref} +\notindent \notag \usepackage{amsmath} \usepackage[a4paper,margin=1in,portrait]{geometry} +\author{Elizabeth Hunt} +\date{\today} +\title{Homework 5} +\hypersetup{ + pdfauthor={Elizabeth Hunt}, + pdftitle={Homework 5}, + pdfkeywords={}, + pdfsubject={}, + pdfcreator={Emacs 28.2 (Org mode 9.7-pre)}, + pdflang={English}} +\begin{document} + +\maketitle +\setlength\parindent{0pt} + +\section{Question One} +\label{sec:org4e80298} +See LIZFCM \(\rightarrow\) Matrix Routines \(\rightarrow\) \texttt{lu decomp} \& \texttt{bsubst}. + +The test \texttt{UTEST(matrix, lu\_decomp)} is a unit test for the \texttt{lu\_decomp} routine, +and \texttt{UTEST(matrix, bsubst)} verifies back substitution on an upper triangular +3 \texttimes{} 3 matrix with a known solution that can be verified manually. + +Both can be found in \texttt{tests/matrix.t.c}. + +\section{Question Two} +\label{sec:orga73d05c} +Unless the following are met, the resulting solution will be garbage. + +\begin{enumerate} +\item The matrix \(U\) must be not be singular. +\item \(U\) must be square (or it will fail the \texttt{assert}). +\item The system created by \(Ux = b\) must be consistent. +\item \(U\) is (quite obviously) in upper-triangular form. +\end{enumerate} + +Thus, the actual calculation performing the \(LU\) decomposition +(in \texttt{lu\_decomp}) does a sanity +check for 1-3 will fail an assert, should a point along the diagonal (pivot) be +zero, or the matrix be non-factorable. + +\section{Question Three} +\label{sec:org35163c5} +See LIZFCM \(\rightarrow\) Matrix Routines \(\rightarrow\) \texttt{fsubst}. + +\texttt{UTEST(matrix, fsubst)} verifies forward substitution on a lower triangular 3 \texttimes{} 3 +matrix with a known solution that can be verified manually. + +\section{Question Four} +\label{sec:org79d9061} + +See LIZFCM \(\rightarrow\) Matrix Routines \(\rightarrow\) \texttt{gaussian\_elimination} and \texttt{solve\_gaussian\_elimination}. + +\section{Question Five} +\label{sec:orgc6ac464} +See LIZFCM \(\rightarrow\) Matrix Routines \(\rightarrow\) \texttt{m\_dot\_v}, and the \texttt{UTEST(matrix, m\_dot\_v)} in +\texttt{tests/matrix.t.c}. + +\section{Question Six} +\label{sec:org66fedab} +See \texttt{UTEST(matrix, solve\_gaussian\_elimination)} in \texttt{tests/matrix.t.c}, which generates a diagonally dominant 10 \texttimes{} 10 matrix +and shows that the solution is consistent with the initial matrix, according to the steps given. Then, +we do a dot product between each row of the diagonally dominant matrix and the solution vector to ensure +it is near equivalent to the input vector. + +\section{Question Seven} +\label{sec:org6897ff2} +See \texttt{UTEST(matrix, solve\_matrix\_lu\_bsubst)} which does the same test in Question Six with the solution according to +\texttt{solve\_matrix\_lu\_bsubst} as shown in the Software Manual. + +\section{Question Eight} +\label{sec:org5d529dd} +No, since the time complexity for Gaussian Elimination is always less than that of the LU factorization solution by \(O(n^2)\) operations +(in LU factorization we perform both backwards and forwards substitutions proceeding the LU decomp, in Gaussian Elimination we only need +back substitution). + +\section{Question Nine, Ten} +\label{sec:org0fb8e09} +See LIZFCM Software manual and shared library in \texttt{dist} after compiling. +\end{document}
\ No newline at end of file diff --git a/Homework/math4610/homeworks/hw-6.org b/Homework/math4610/homeworks/hw-6.org new file mode 100644 index 0000000..eebc0c2 --- /dev/null +++ b/Homework/math4610/homeworks/hw-6.org @@ -0,0 +1,199 @@ +#+TITLE: Homework 6 +#+AUTHOR: Elizabeth Hunt +#+LATEX_HEADER: \notindent \notag \usepackage{amsmath} \usepackage[a4paper,margin=1in,portrait]{geometry} +#+LATEX: \setlength\parindent{0pt} +#+OPTIONS: toc:nil + +* Question One + +For $g(x) = x + f(x)$ then we know $g'(x) = 1 + 2x - 5$ and thus $|g'(x)| \lt 1$ is only true +on the interval $(1.5, 2.5)$, and for $g(x) = x - f(x)$ then we know $g'(x) = 1 - (2x - 5)$ +and thus $|g'(x)| < 1$ is only true on the interval $(2.5, 3.5)$. + +Because we know the roots of $f$ are $2, 3$ ($f(x) = (x-2)(x-3)$) then we can only be +certain that $g(x) = x + f(x)$ will converge to the root $2$ if we pick an initial +guess between $(1.5, 2.5)$, and likewise for $g(x) = x - f(x)$, $3$: + +#+BEGIN_SRC c + // tests/roots.t.c + UTEST(root, fixed_point_iteration_method) { + // x^2 - 5x + 6 = (x - 3)(x - 2) + double expect_x1 = 3.0; + double expect_x2 = 2.0; + + double tolerance = 0.001; + uint64_t max_iterations = 10; + + double x_0 = 1.55; // 1.5 < 1.55 < 2.5 + // g1(x) = x + f(x) + double root1 = + fixed_point_iteration_method(&f2, &g1, x_0, tolerance, max_iterations); + EXPECT_NEAR(root1, expect_x2, tolerance); + + // g2(x) = x - f(x) + x_0 = 3.4; // 2.5 < 3.4 < 3.5 + double root2 = + fixed_point_iteration_method(&f2, &g2, x_0, tolerance, max_iterations); + EXPECT_NEAR(root2, expect_x1, tolerance); + } +#+END_SRC + +And by this method passing in ~tests/roots.t.c~ we know they converged within ~tolerance~ before +10 iterations. + +* Question Two + +Yes, we showed that for $\epsilon = 1$ in Question One, we can converge upon a root in the range $(2.5, 3.5)$, and +when $\epsilon = -1$ we can converge upon a root in the range $(1.5, 2.5)$. + +See the above unit tests in Question One for each $\epsilon$. + +* Question Three + +See ~test/roots.t.c -> UTEST(root, bisection_with_error_assumption)~ +and the software manual entry ~bisect_find_root_with_error_assumption~. + +* Question Four + +See ~test/roots.t.c -> UTEST(root, fixed_point_newton_method)~ +and the software manual entry ~fixed_point_newton_method~. + +* Question Five + +See ~test/roots.t.c -> UTEST(root, fixed_point_secant_method)~ +and the software manual entry ~fixed_point_secant_method~. + +* Question Six + +See ~test/roots.t.c -> UTEST(root, fixed_point_bisection_secant_method)~ +and the software manual entry ~fixed_point_bisection_secant_method~. + +* Question Seven + +The existance of ~test/roots.t.c~'s compilation into ~dist/lizfcm.test~ via ~make~ +shows that the compiled ~lizfcm.a~ contains the root methods mentioned; a user +could link the library and use them, as we do in Question Eight. + +* Question Eight + +The given ODE $\frac{dP}{dt} = \alpha P - \beta P$ has a trivial solution by separation: + +\begin{equation*} +P(t) = C e^{t(\alpha - \beta)} +\end{equation*} + +And + +\begin{equation*} +P_0 = P(0) = C e^0 = C +\end{equation*} + +So $P(t) = P_0 e^{t(\alpha - \beta)}$. + +We're trying to find $t$ such that $P(t) = P_\infty$, thus we're finding roots of $P(t) - P_\infty$. + +The following code (in ~homeworks/hw_6_p_8.c~) produces this output: + +\begin{verbatim} +$ gcc -I../inc/ -Wall hw_6_p_8.c ../lib/lizfcm.a -lm -o hw_6_p_8 && ./hw_6_p_8 +a ~ 27.269515; P(27.269515) - P_infty = -0.000000 +b ~ 40.957816; P(40.957816) - P_infty = -0.000000 +c ~ 40.588827; P(40.588827) - P_infty = -0.000000 +d ~ 483.611967; P(483.611967) - P_infty = -0.000000 +e ~ 4.894274; P(4.894274) - P_infty = -0.000000 + +\end{verbatim} + +#+BEGIN_SRC c +// compile & test w/ +// \--> gcc -I../inc/ -Wall hw_6_p_8.c ../lib/lizfcm.a -lm -o hw_6_p_8 +// \--> ./hw_6_p_8 + +#include "lizfcm.h" +#include <math.h> +#include <stdio.h> + +double a(double t) { + double alpha = 0.1; + double beta = 0.001; + double p_0 = 2; + double p_infty = 29.75; + + return p_0 * exp(t * (alpha - beta)) - p_infty; +} + +double b(double t) { + double alpha = 0.1; + double beta = 0.001; + double p_0 = 2; + double p_infty = 115.35; + + return p_0 * exp(t * (alpha - beta)) - p_infty; +} + +double c(double t) { + double alpha = 0.1; + double beta = 0.0001; + double p_0 = 2; + double p_infty = 115.35; + + return p_0 * exp(t * (alpha - beta)) - p_infty; +} + +double d(double t) { + double alpha = 0.01; + double beta = 0.001; + double p_0 = 2; + double p_infty = 155.346; + + return p_0 * exp(t * (alpha - beta)) - p_infty; +} + +double e(double t) { + double alpha = 0.1; + double beta = 0.01; + double p_0 = 100; + double p_infty = 155.346; + + return p_0 * exp(t * (alpha - beta)) - p_infty; +} + +int main() { + uint64_t max_iterations = 1000; + double tolerance = 0.0000001; + + Array_double *ivt_range = find_ivt_range(&a, -5.0, 3.0, 1000); + double approx_a = fixed_point_secant_bisection_method( + &a, ivt_range->data[0], ivt_range->data[1], tolerance, max_iterations); + + free_vector(ivt_range); + ivt_range = find_ivt_range(&b, -5.0, 3.0, 1000); + double approx_b = fixed_point_secant_bisection_method( + &b, ivt_range->data[0], ivt_range->data[1], tolerance, max_iterations); + + free_vector(ivt_range); + ivt_range = find_ivt_range(&c, -5.0, 3.0, 1000); + double approx_c = fixed_point_secant_bisection_method( + &c, ivt_range->data[0], ivt_range->data[1], tolerance, max_iterations); + + free_vector(ivt_range); + ivt_range = find_ivt_range(&d, -5.0, 3.0, 1000); + double approx_d = fixed_point_secant_bisection_method( + &d, ivt_range->data[0], ivt_range->data[1], tolerance, max_iterations); + + free_vector(ivt_range); + ivt_range = find_ivt_range(&e, -5.0, 3.0, 1000); + double approx_e = fixed_point_secant_bisection_method( + &e, ivt_range->data[0], ivt_range->data[1], tolerance, max_iterations); + + printf("a ~ %f; P(%f) = %f\n", approx_a, approx_a, a(approx_a)); + printf("b ~ %f; P(%f) = %f\n", approx_b, approx_b, b(approx_b)); + printf("c ~ %f; P(%f) = %f\n", approx_c, approx_c, c(approx_c)); + printf("d ~ %f; P(%f) = %f\n", approx_d, approx_d, d(approx_d)); + printf("e ~ %f; P(%f) = %f\n", approx_e, approx_e, e(approx_e)); + + return 0; +} +#+END_SRC + + diff --git a/Homework/math4610/homeworks/hw-6.pdf b/Homework/math4610/homeworks/hw-6.pdf Binary files differnew file mode 100644 index 0000000..c056102 --- /dev/null +++ b/Homework/math4610/homeworks/hw-6.pdf diff --git a/Homework/math4610/homeworks/hw-6.tex b/Homework/math4610/homeworks/hw-6.tex new file mode 100644 index 0000000..1a0ddc4 --- /dev/null +++ b/Homework/math4610/homeworks/hw-6.tex @@ -0,0 +1,223 @@ +% Created 2023-11-11 Sat 13:13 +% Intended LaTeX compiler: pdflatex +\documentclass[11pt]{article} +\usepackage[utf8]{inputenc} +\usepackage[T1]{fontenc} +\usepackage{graphicx} +\usepackage{longtable} +\usepackage{wrapfig} +\usepackage{rotating} +\usepackage[normalem]{ulem} +\usepackage{amsmath} +\usepackage{amssymb} +\usepackage{capt-of} +\usepackage{hyperref} +\notindent \notag \usepackage{amsmath} \usepackage[a4paper,margin=1in,portrait]{geometry} +\author{Elizabeth Hunt} +\date{\today} +\title{Homework 6} +\hypersetup{ + pdfauthor={Elizabeth Hunt}, + pdftitle={Homework 6}, + pdfkeywords={}, + pdfsubject={}, + pdfcreator={Emacs 29.1 (Org mode 9.7-pre)}, + pdflang={English}} +\begin{document} + +\maketitle +\setlength\parindent{0pt} +\section{Question One} +\label{sec:org206b859} + +For \(g(x) = x + f(x)\) then we know \(g'(x) = 1 + 2x - 5\) and thus \(|g'(x)| \lt 1\) is only true +on the interval \((1.5, 2.5)\), and for \(g(x) = x - f(x)\) then we know \(g'(x) = 1 - (2x - 5)\) +and thus \(|g'(x)| < 1\) is only true on the interval \((2.5, 3.5)\). + +Because we know the roots of \(f\) are \(2, 3\) (\(f(x) = (x-2)(x-3)\)) then we can only be +certain that \(g(x) = x + f(x)\) will converge to the root \(2\) if we pick an initial +guess between \((1.5, 2.5)\), and likewise for \(g(x) = x - f(x)\), \(3\): + +\begin{verbatim} +// tests/roots.t.c +UTEST(root, fixed_point_iteration_method) { + // x^2 - 5x + 6 = (x - 3)(x - 2) + double expect_x1 = 3.0; + double expect_x2 = 2.0; + + double tolerance = 0.001; + uint64_t max_iterations = 10; + + double x_0 = 1.55; // 1.5 < 1.55 < 2.5 + // g1(x) = x + f(x) + double root1 = + fixed_point_iteration_method(&f2, &g1, x_0, tolerance, max_iterations); + EXPECT_NEAR(root1, expect_x2, tolerance); + + // g2(x) = x - f(x) + x_0 = 3.4; // 2.5 < 3.4 < 3.5 + double root2 = + fixed_point_iteration_method(&f2, &g2, x_0, tolerance, max_iterations); + EXPECT_NEAR(root2, expect_x1, tolerance); +} +\end{verbatim} + +And by this method passing in \texttt{tests/roots.t.c} we know they converged within \texttt{tolerance} before +10 iterations. +\section{Question Two} +\label{sec:orga0f5b42} + +Yes, we showed that for \(\epsilon = 1\) in Question One, we can converge upon a root in the range \((2.5, 3.5)\), and +when \(\epsilon = -1\) we can converge upon a root in the range \((1.5, 2.5)\). + +See the above unit tests in Question One for each \(\epsilon\). +\section{Question Three} +\label{sec:org19aa326} + +See \texttt{test/roots.t.c -> UTEST(root, bisection\_with\_error\_assumption)} +and the software manual entry \texttt{bisect\_find\_root\_with\_error\_assumption}. +\section{Question Four} +\label{sec:org722aa6a} + +See \texttt{test/roots.t.c -> UTEST(root, fixed\_point\_newton\_method)} +and the software manual entry \texttt{fixed\_point\_newton\_method}. +\section{Question Five} +\label{sec:org587ee52} + +See \texttt{test/roots.t.c -> UTEST(root, fixed\_point\_secant\_method)} +and the software manual entry \texttt{fixed\_point\_secant\_method}. +\section{Question Six} +\label{sec:org79bf754} + +See \texttt{test/roots.t.c -> UTEST(root, fixed\_point\_bisection\_secant\_method)} +and the software manual entry \texttt{fixed\_point\_bisection\_secant\_method}. +\section{Question Seven} +\label{sec:org4cb47e5} + +The existance of \texttt{test/roots.t.c}'s compilation into \texttt{dist/lizfcm.test} via \texttt{make} +shows that the compiled \texttt{lizfcm.a} contains the root methods mentioned; a user +could link the library and use them, as we do in Question Eight. +\section{Question Eight} +\label{sec:org4a8160d} + +The given ODE \(\frac{dP}{dt} = \alpha P - \beta P\) has a trivial solution by separation: + +\begin{equation*} +P(t) = C e^{t(\alpha - \beta)} +\end{equation*} + +And + +\begin{equation*} +P_0 = P(0) = C e^0 = C +\end{equation*} + +So \(P(t) = P_0 e^{t(\alpha - \beta)}\). + +We're trying to find \(t\) such that \(P(t) = P_\infty\), thus we're finding roots of \(P(t) - P_\infty\). + +The following code (in \texttt{homeworks/hw\_6\_p\_8.c}) produces this output: + +\begin{verbatim} +$ gcc -I../inc/ -Wall hw_6_p_8.c ../lib/lizfcm.a -lm -o hw_6_p_8 && ./hw_6_p_8 + +a ~ 27.303411; P(27.303411) - P_infty = -0.000000 +b ~ 40.957816; P(40.957816) - P_infty = -0.000000 +c ~ 40.588827; P(40.588827) - P_infty = -0.000000 +d ~ 483.611967; P(483.611967) - P_infty = -0.000000 +e ~ 4.894274; P(4.894274) - P_infty = -0.000000 + +\end{verbatim} + +\begin{verbatim} +// compile & test w/ +// \--> gcc -I../inc/ -Wall hw_6_p_8.c ../lib/lizfcm.a -lm -o hw_6_p_8 +// \--> ./hw_6_p_8 + +#include "lizfcm.h" +#include <math.h> +#include <stdio.h> + +double a(double t) { + double alpha = 0.1; + double beta = 0.001; + double p_0 = 2; + double p_infty = 29.85; + + return p_0 * exp(t * (alpha - beta)) - p_infty; +} + +double b(double t) { + double alpha = 0.1; + double beta = 0.001; + double p_0 = 2; + double p_infty = 115.35; + + return p_0 * exp(t * (alpha - beta)) - p_infty; +} + +double c(double t) { + double alpha = 0.1; + double beta = 0.0001; + double p_0 = 2; + double p_infty = 115.35; + + return p_0 * exp(t * (alpha - beta)) - p_infty; +} + +double d(double t) { + double alpha = 0.01; + double beta = 0.001; + double p_0 = 2; + double p_infty = 155.346; + + return p_0 * exp(t * (alpha - beta)) - p_infty; +} + +double e(double t) { + double alpha = 0.1; + double beta = 0.01; + double p_0 = 100; + double p_infty = 155.346; + + return p_0 * exp(t * (alpha - beta)) - p_infty; +} + +int main() { + uint64_t max_iterations = 1000; + double tolerance = 0.0000001; + + Array_double *ivt_range = find_ivt_range(&a, -5.0, 3.0, 1000); + double approx_a = fixed_point_secant_bisection_method( + &a, ivt_range->data[0], ivt_range->data[1], tolerance, max_iterations); + + free_vector(ivt_range); + ivt_range = find_ivt_range(&b, -5.0, 3.0, 1000); + double approx_b = fixed_point_secant_bisection_method( + &b, ivt_range->data[0], ivt_range->data[1], tolerance, max_iterations); + + free_vector(ivt_range); + ivt_range = find_ivt_range(&c, -5.0, 3.0, 1000); + double approx_c = fixed_point_secant_bisection_method( + &c, ivt_range->data[0], ivt_range->data[1], tolerance, max_iterations); + + free_vector(ivt_range); + ivt_range = find_ivt_range(&d, -5.0, 3.0, 1000); + double approx_d = fixed_point_secant_bisection_method( + &d, ivt_range->data[0], ivt_range->data[1], tolerance, max_iterations); + + free_vector(ivt_range); + ivt_range = find_ivt_range(&e, -5.0, 3.0, 1000); + double approx_e = fixed_point_secant_bisection_method( + &e, ivt_range->data[0], ivt_range->data[1], tolerance, max_iterations); + + printf("a ~ %f; P(%f) = %f\n", approx_a, approx_a, a(approx_a)); + printf("b ~ %f; P(%f) = %f\n", approx_b, approx_b, b(approx_b)); + printf("c ~ %f; P(%f) = %f\n", approx_c, approx_c, c(approx_c)); + printf("d ~ %f; P(%f) = %f\n", approx_d, approx_d, d(approx_d)); + printf("e ~ %f; P(%f) = %f\n", approx_e, approx_e, e(approx_e)); + + return 0; +} +\end{verbatim} +\end{document} diff --git a/Homework/math4610/homeworks/hw-7.org b/Homework/math4610/homeworks/hw-7.org new file mode 100644 index 0000000..2c28af2 --- /dev/null +++ b/Homework/math4610/homeworks/hw-7.org @@ -0,0 +1,76 @@ +#+TITLE: Homework 7 +#+AUTHOR: Elizabeth Hunt +#+LATEX_HEADER: \notindent \notag \usepackage{amsmath} \usepackage[a4paper,margin=1in,portrait]{geometry} +#+LATEX: \setlength\parindent{0pt} +#+OPTIONS: toc:nil + +* Question One +See ~UTEST(eigen, dominant_eigenvalue)~ in ~test/eigen.t.c~ and the entry +~Eigen-Adjacent -> dominant_eigenvalue~ in the LIZFCM API documentation. +* Question Two +See ~UTEST(eigen, leslie_matrix_dominant_eigenvalue)~ in ~test/eigen.t.c~ +and the entry ~Eigen-Adjacent -> leslie_matrix~ in the LIZFCM API +documentation. +* Question Three +See ~UTEST(eigen, least_dominant_eigenvalue)~ in ~test/eigen.t.c~ which +finds the least dominant eigenvalue on the matrix: + +\begin{bmatrix} +2 & 2 & 4 \\ +1 & 4 & 7 \\ +0 & 2 & 6 +\end{bmatrix} + +which has eigenvalues: $5 + \sqrt{17}, 2, 5 - \sqrt{17}$ and should thus produce $5 - \sqrt{17}$. + +See also the entry ~Eigen-Adjacent -> least_dominant_eigenvalue~ in the LIZFCM API +documentation. +* Question Four +See ~UTEST(eigen, shifted_eigenvalue)~ in ~test/eigen.t.c~ which +finds the least dominant eigenvalue on the matrix: + +\begin{bmatrix} +2 & 2 & 4 \\ +1 & 4 & 7 \\ +0 & 2 & 6 +\end{bmatrix} + +which has eigenvalues: $5 + \sqrt{17}, 2, 5 - \sqrt{17}$ and should thus produce $2.0$. + +With the initial guess: $[0.5, 1.0, 0.75]$. + +See also the entry ~Eigen-Adjacent -> shift_inverse_power_eigenvalue~ in the LIZFCM API +documentation. +* Question Five +See ~UTEST(eigen, partition_find_eigenvalues)~ in ~test/eigen.t.c~ which +finds the eigenvalues in a partition of 10 on the matrix: + +\begin{bmatrix} +2 & 2 & 4 \\ +1 & 4 & 7 \\ +0 & 2 & 6 +\end{bmatrix} + +which has eigenvalues: $5 + \sqrt{17}, 2, 5 - \sqrt{17}$, and should produce all three from +the partitions when given the guesses $[0.5, 1.0, 0.75]$ from the questions above. + +See also the entry ~Eigen-Adjacent -> partition_find_eigenvalues~ in the LIZFCM API +documentation. + +* Question Six +Consider we have the results of two methods developed in this homework: ~least_dominant_eigenvalue~, and ~dominant_eigenvalue~ +into ~lambda_0~, ~lambda_n~, respectively. Also assume that we have the method implemented as we've introduced, +~shift_inverse_power_eigenvalue~. + +Then, we begin at the midpoint of ~lambda_0~ and ~lambda_n~, and compute the +~new_lambda = shift_inverse_power_eigenvalue~ +with a shift at the midpoint, and some given initial guess. + +1. If the result is equal (or within some tolerance) to ~lambda_n~ then the closest eigenvalue to the midpoint + is still the dominant eigenvalue, and thus the next most dominant will be on the left. Set ~lambda_n~ + to the midpoint and reiterate. +2. If the result is greater or equal to ~lambda_0~ we know an eigenvalue of greater or equal magnitude + exists on the right. So, we set ~lambda_0~ to this eigenvalue associated with the midpoint, and + re-iterate. +3. Continue re-iterating until we hit some given maximum number of iterations. Finally we will return + ~new_lambda~. diff --git a/Homework/math4610/homeworks/hw-7.pdf b/Homework/math4610/homeworks/hw-7.pdf Binary files differnew file mode 100644 index 0000000..4003f59 --- /dev/null +++ b/Homework/math4610/homeworks/hw-7.pdf diff --git a/Homework/math4610/homeworks/hw-7.tex b/Homework/math4610/homeworks/hw-7.tex new file mode 100644 index 0000000..be3fde4 --- /dev/null +++ b/Homework/math4610/homeworks/hw-7.tex @@ -0,0 +1,107 @@ +% Created 2023-11-27 Mon 15:13 +% Intended LaTeX compiler: pdflatex +\documentclass[11pt]{article} +\usepackage[utf8]{inputenc} +\usepackage[T1]{fontenc} +\usepackage{graphicx} +\usepackage{longtable} +\usepackage{wrapfig} +\usepackage{rotating} +\usepackage[normalem]{ulem} +\usepackage{amsmath} +\usepackage{amssymb} +\usepackage{capt-of} +\usepackage{hyperref} +\notindent \notag \usepackage{amsmath} \usepackage[a4paper,margin=1in,portrait]{geometry} +\author{Elizabeth Hunt} +\date{\today} +\title{Homework 7} +\hypersetup{ + pdfauthor={Elizabeth Hunt}, + pdftitle={Homework 7}, + pdfkeywords={}, + pdfsubject={}, + pdfcreator={Emacs 29.1 (Org mode 9.7-pre)}, + pdflang={English}} +\begin{document} + +\maketitle +\setlength\parindent{0pt} +\section{Question One} +\label{sec:org8ef0ee6} +See \texttt{UTEST(eigen, dominant\_eigenvalue)} in \texttt{test/eigen.t.c} and the entry +\texttt{Eigen-Adjacent -> dominant\_eigenvalue} in the LIZFCM API documentation. +\section{Question Two} +\label{sec:orgbdba5c1} +See \texttt{UTEST(eigen, leslie\_matrix\_dominant\_eigenvalue)} in \texttt{test/eigen.t.c} +and the entry \texttt{Eigen-Adjacent -> leslie\_matrix} in the LIZFCM API +documentation. +\section{Question Three} +\label{sec:org19b04f4} +See \texttt{UTEST(eigen, least\_dominant\_eigenvalue)} in \texttt{test/eigen.t.c} which +finds the least dominant eigenvalue on the matrix: + +\begin{bmatrix} +2 & 2 & 4 \\ +1 & 4 & 7 \\ +0 & 2 & 6 +\end{bmatrix} + +which has eigenvalues: \(5 + \sqrt{17}, 2, 5 - \sqrt{17}\) and should thus produce \(5 - \sqrt{17}\). + +See also the entry \texttt{Eigen-Adjacent -> least\_dominant\_eigenvalue} in the LIZFCM API +documentation. +\section{Question Four} +\label{sec:orgc58d42d} +See \texttt{UTEST(eigen, shifted\_eigenvalue)} in \texttt{test/eigen.t.c} which +finds the least dominant eigenvalue on the matrix: + +\begin{bmatrix} +2 & 2 & 4 \\ +1 & 4 & 7 \\ +0 & 2 & 6 +\end{bmatrix} + +which has eigenvalues: \(5 + \sqrt{17}, 2, 5 - \sqrt{17}\) and should thus produce \(2.0\). + +With the initial guess: \([0.5, 1.0, 0.75]\). + +See also the entry \texttt{Eigen-Adjacent -> shift\_inverse\_power\_eigenvalue} in the LIZFCM API +documentation. +\section{Question Five} +\label{sec:orga369221} +See \texttt{UTEST(eigen, partition\_find\_eigenvalues)} in \texttt{test/eigen.t.c} which +finds the eigenvalues in a partition of 10 on the matrix: + +\begin{bmatrix} +2 & 2 & 4 \\ +1 & 4 & 7 \\ +0 & 2 & 6 +\end{bmatrix} + +which has eigenvalues: \(5 + \sqrt{17}, 2, 5 - \sqrt{17}\), and should produce all three from +the partitions when given the guesses \([0.5, 1.0, 0.75]\) from the questions above. + +See also the entry \texttt{Eigen-Adjacent -> partition\_find\_eigenvalues} in the LIZFCM API +documentation. +\section{Question Six} +\label{sec:orgadc3078} +Consider we have the results of two methods developed in this homework: \texttt{least\_dominant\_eigenvalue}, and \texttt{dominant\_eigenvalue} +into \texttt{lambda\_0}, \texttt{lambda\_n}, respectively. Also assume that we have the method implemented as we've introduced, +\texttt{shift\_inverse\_power\_eigenvalue}. + +Then, we begin at the midpoint of \texttt{lambda\_0} and \texttt{lambda\_n}, and compute the +\texttt{new\_lambda = shift\_inverse\_power\_eigenvalue} +with a shift at the midpoint, and some given initial guess. + +\begin{enumerate} +\item If the result is equal (or within some tolerance) to \texttt{lambda\_n} then the closest eigenvalue to the midpoint +is still the dominant eigenvalue, and thus the next most dominant will be on the left. Set \texttt{lambda\_n} +to the midpoint and reiterate. +\item If the result is greater or equal to \texttt{lambda\_0} we know an eigenvalue of greater or equal magnitude +exists on the right. So, we set \texttt{lambda\_0} to this eigenvalue associated with the midpoint, and +re-iterate. +\item Continue re-iterating until we hit some given maximum number of iterations. Finally we will return +\texttt{new\_lambda}. +\end{enumerate} +\end{document} diff --git a/Homework/math4610/homeworks/hw-8.org b/Homework/math4610/homeworks/hw-8.org new file mode 100644 index 0000000..10c7dd8 --- /dev/null +++ b/Homework/math4610/homeworks/hw-8.org @@ -0,0 +1,311 @@ +#+TITLE: Homework 8 +#+AUTHOR: Elizabeth Hunt +#+LATEX_HEADER: \notindent \notag \usepackage{amsmath} \usepackage[a4paper,margin=1in,portrait]{geometry} +#+LATEX: \setlength\parindent{0pt} +#+OPTIONS: toc:nil + +* Question One +See ~UTEST(jacobi, solve_jacobi)~ in ~test/jacobi.t.c~ and the entry +~Jacobi / Gauss-Siedel -> solve_jacobi~ in the LIZFCM API documentation. +* Question Two +We cannot just perform the Jacobi algorithm on a Leslie matrix since +it is obviously not diagonally dominant - which is a requirement. It is +certainly not always the case, but, if a Leslie matrix $L$ is invertible, we can +first perform gaussian elimination on $L$ augmented with $n_{k+1}$ +to obtain $n_k$ with the Jacobi method. See ~UTEST(jacobi, leslie_solve)~ +in ~test/jacobi.t.c~ for an example wherein this method is tested on a Leslie +matrix to recompute a given initial population distribution. + +In terms of accuracy, an LU factorization and back substitution approach will +always be as correct as possible within the limits of computation; it's a +direct solution method. It's simply the nature of the Jacobi algorithm being +a convergent solution that determines its accuracy. + +LU factorization also performs in order $O(n^3)$ runtime for an $n \times n$ +matrix, whereas the Jacobi algorithm runs in order $O(k n^2) = O(n^2)$ on average +but with the con that $k$ is given by some function on both the convergence criteria and the number of +nonzero entries in the matrix - which might end up worse in some cases than the LU decomp approach. + +* Question Three +See ~UTEST(jacobi, gauss_siedel_solve)~ in ~test/jacobi.t.c~ which runs the same +unit test as ~UTEST(jacobi, solve_jacobi)~ but using the +~Jacobi / Gauss-Siedel -> gauss_siedel_solve~ method as documented in the LIZFCM API reference. + +* Question Four, Five +We produce the following operation counts (by hackily adding the operation count as the last element +to the solution vector) and errors - the sum of each vector elements' absolute value away from 1.0 +using the proceeding patch and unit test. + +| N | JAC opr | JAC err | GS opr | GS err | LU opr | LU err | +| 5 | 1622 | 0.001244 | 577 | 0.000098 | 430 | 0.000000 | +| 6 | 2812 | 0.001205 | 775 | 0.000080 | 681 | 0.000000 | +| 7 | 5396 | 0.001187 | 860 | 0.000178 | 1015 | 0.000000 | +| 8 | 5618 | 0.001468 | 1255 | 0.000121 | 1444 | 0.000000 | +| 9 | 7534 | 0.001638 | 1754 | 0.000091 | 1980 | 0.000000 | +| 10 | 10342 | 0.001425 | 1847 | 0.000435 | 2635 | 0.000000 | +| 11 | 12870 | 0.001595 | 2185 | 0.000368 | 3421 | 0.000000 | +| 12 | 17511 | 0.001860 | 2912 | 0.000322 | 4350 | 0.000000 | +| 13 | 16226 | 0.001631 | 3362 | 0.000270 | 5434 | 0.000000 | +| 14 | 34333 | 0.001976 | 3844 | 0.000121 | 6685 | 0.000000 | +| 15 | 38474 | 0.001922 | 4358 | 0.000311 | 8115 | 0.000000 | +| 16 | 40405 | 0.002061 | 4904 | 0.000204 | 9736 | 0.000000 | +| 17 | 58518 | 0.002125 | 5482 | 0.000311 | 11560 | 0.000000 | +| 18 | 68079 | 0.002114 | 6092 | 0.000279 | 13599 | 0.000000 | +| 19 | 95802 | 0.002159 | 6734 | 0.000335 | 15865 | 0.000000 | +| 20 | 85696 | 0.002141 | 7408 | 0.000289 | 18370 | 0.000000 | +| 21 | 89026 | 0.002316 | 8114 | 0.000393 | 21126 | 0.000000 | +| 22 | 101537 | 0.002344 | 8852 | 0.000414 | 24145 | 0.000000 | +| 23 | 148040 | 0.002323 | 9622 | 0.000230 | 27439 | 0.000000 | +| 24 | 137605 | 0.002348 | 10424 | 0.000213 | 31020 | 0.000000 | +| 25 | 169374 | 0.002409 | 11258 | 0.000894 | 34900 | 0.000000 | +| 26 | 215166 | 0.002502 | 12124 | 0.000564 | 39091 | 0.000000 | +| 27 | 175476 | 0.002616 | 13022 | 0.000535 | 43605 | 0.000000 | +| 28 | 268454 | 0.002651 | 13952 | 0.000690 | 48454 | 0.000000 | +| 29 | 267034 | 0.002697 | 14914 | 0.000675 | 53650 | 0.000000 | +| 30 | 277193 | 0.002686 | 15908 | 0.000542 | 59205 | 0.000000 | +| 31 | 336792 | 0.002736 | 16934 | 0.000390 | 65131 | 0.000000 | +| 32 | 293958 | 0.002741 | 17992 | 0.000660 | 71440 | 0.000000 | +| 33 | 323638 | 0.002893 | 19082 | 0.001072 | 78144 | 0.000000 | +| 34 | 375104 | 0.003001 | 20204 | 0.001018 | 85255 | 0.000000 | +| 35 | 436092 | 0.003004 | 21358 | 0.000912 | 92785 | 0.000000 | +| 36 | 538143 | 0.003005 | 22544 | 0.000954 | 100746 | 0.000000 | +| 37 | 511886 | 0.003029 | 23762 | 0.000462 | 109150 | 0.000000 | +| 38 | 551332 | 0.003070 | 25012 | 0.000996 | 118009 | 0.000000 | +| 39 | 592750 | 0.003110 | 26294 | 0.000989 | 127335 | 0.000000 | +| 40 | 704208 | 0.003165 | 27608 | 0.000583 | 137140 | 0.000000 | + +#+BEGIN_SRC +diff --git a/src/matrix.c b/src/matrix.c +index 901a426..af5529f 100644 +--- a/src/matrix.c ++++ b/src/matrix.c +@@ -144,20 +144,54 @@ Array_double *solve_matrix_lu_bsubst(Matrix_double *m, Array_double *b) { + assert(b->size == m->rows); + assert(m->rows == m->cols); + ++ double opr = 0; ++ ++ opr += b->size; + Array_double *x = copy_vector(b); ++ ++ size_t n = m->rows; ++ opr += n * n; // (u copy) ++ opr += n * n; // l_empty ++ opr += n * n + n; // copy + put_identity_diagonal ++ opr += n; // pivot check ++ opr += m->cols; ++ for (size_t x = 0; x < m->cols; x++) { ++ opr += (m->rows - (x + 1)); ++ for (size_t y = x + 1; y < m->rows; y++) { ++ opr += 1; ++ opr += 2; // -factor ++ opr += 4 * n; // scale, add_v, free_vector ++ opr += 1; // -factor ++ } ++ } ++ opr += n; + Matrix_double **u_l = lu_decomp(m); ++ + Matrix_double *u = u_l[0]; + Matrix_double *l = u_l[1]; + ++ opr += n; ++ for (int64_t row = n - 1; row >= 0; row--) { ++ opr += 2 * (n - row); ++ opr += 1; ++ } + Array_double *b_fsub = fsubst(l, b); ++ ++ opr += n; ++ for (size_t x = 0; x < n; x++) { ++ opr += 2 * (x + 1); ++ opr += 1; // /= l->data[row]->data[row] ++ } + x = bsubst(u, b_fsub); +- free_vector(b_fsub); + ++ free_vector(b_fsub); + free_matrix(u); + free_matrix(l); + free(u_l); + +- return x; ++ Array_double *copy = add_element(x, opr); ++ free_vector(x); ++ return copy; + } + + Matrix_double *gaussian_elimination(Matrix_double *m) { +@@ -231,18 +265,36 @@ Array_double *jacobi_solve(Matrix_double *m, Array_double *b, + assert(b->size == m->cols); + size_t iter = max_iterations; + ++ double opr = 0; ++ ++ opr += 2 * b->size; // to initialize two vectors with the same dim of b twice + Array_double *x_k = InitArrayWithSize(double, b->size, 0.0); + Array_double *x_k_1 = + InitArrayWithSize(double, b->size, rand_from(0.1, 10.0)); + ++ // add since these wouldn't be accounter for after the loop ++ opr += 1; // iter decrement ++ opr += ++ 3 * x_k_1->size; // 1 to perform x_k_1, x_k and 2 to perform ||x_k_1||_2 + while ((--iter) > 0 && l2_distance(x_k_1, x_k) > l2_convergence_tolerance) { ++ opr += 1; // iter decrement ++ opr += ++ 3 * x_k_1->size; // 1 to perform x_k_1, x_k and 2 to perform ||x_k_1||_2 ++ ++ opr += m->rows; // row for add oprs + for (size_t i = 0; i < m->rows; i++) { + double delta = 0.0; ++ ++ opr += m->cols; + for (size_t j = 0; j < m->cols; j++) { + if (i == j) + continue; ++ ++ opr += 1; + delta += m->data[i]->data[j] * x_k->data[j]; + } ++ ++ opr += 2; + x_k_1->data[i] = (b->data[i] - delta) / m->data[i]->data[i]; + } + +@@ -251,8 +303,9 @@ Array_double *jacobi_solve(Matrix_double *m, Array_double *b, + x_k_1 = tmp; + } + +- free_vector(x_k); +- return x_k_1; ++ Array_double *copy = add_element(x_k_1, opr); ++ free_vector(x_k_1); ++ return copy; + } + + Array_double *gauss_siedel_solve(Matrix_double *m, Array_double *b, +@@ -262,30 +315,48 @@ Array_double *gauss_siedel_solve(Matrix_double *m, Array_double *b, + assert(b->size == m->cols); + size_t iter = max_iterations; + ++ double opr = 0; ++ ++ opr += 2 * b->size; // to initialize two vectors with the same dim of b twice + Array_double *x_k = InitArrayWithSize(double, b->size, 0.0); + Array_double *x_k_1 = + InitArrayWithSize(double, b->size, rand_from(0.1, 10.0)); + + while ((--iter) > 0) { ++ opr += 1; // iter decrement ++ ++ opr += x_k->size; // copy oprs + for (size_t i = 0; i < x_k->size; i++) + x_k->data[i] = x_k_1->data[i]; + ++ opr += m->rows; // row for add oprs + for (size_t i = 0; i < m->rows; i++) { + double delta = 0.0; ++ ++ opr += m->cols; + for (size_t j = 0; j < m->cols; j++) { + if (i == j) + continue; ++ ++ opr += 1; + delta += m->data[i]->data[j] * x_k_1->data[j]; + } ++ ++ opr += 2; + x_k_1->data[i] = (b->data[i] - delta) / m->data[i]->data[i]; + } + ++ opr += ++ 3 * x_k_1->size; // 1 to perform x_k_1, x_k and 2 to perform ||x_k_1||_2 + if (l2_distance(x_k_1, x_k) <= l2_convergence_tolerance) + break; + } + + free_vector(x_k); +- return x_k_1; ++ ++ Array_double *copy = add_element(x_k_1, opr); ++ free_vector(x_k_1); ++ return copy; + } +#+END_SRC + + +And this unit test: +#+BEGIN_SRC c +UTEST(hw_8, p4_5) { + printf("| N | JAC opr | JAC err | GS opr | GS err | LU opr | LU err | \n"); + + for (size_t i = 5; i < 100; i++) { + Matrix_double *m = generate_ddm(i); + double oprs[3] = {0.0, 0.0, 0.0}; + double errs[3] = {0.0, 0.0, 0.0}; + + Array_double *b_1 = InitArrayWithSize(double, m->rows, 1.0); + Array_double *b = m_dot_v(m, b_1); + double tolerance = 0.001; + size_t max_iter = 400; + + // JACOBI + { + Array_double *solution_with_opr_count = + jacobi_solve(m, b, tolerance, max_iter); + Array_double *solution = slice_element(solution_with_opr_count, + solution_with_opr_count->size - 1); + + for (size_t i = 0; i < solution->size; i++) + errs[0] += fabs(solution->data[i] - 1.0); + + oprs[0] = + solution_with_opr_count->data[solution_with_opr_count->size - 1]; + + free_vector(solution); + free_vector(solution_with_opr_count); + } + + // GAUSS-SIEDEL + { + Array_double *solution_with_opr_count = + gauss_siedel_solve(m, b, tolerance, max_iter); + Array_double *solution = slice_element(solution_with_opr_count, + solution_with_opr_count->size - 1); + + for (size_t i = 0; i < solution->size; i++) + errs[1] += fabs(solution->data[i] - 1.0); + + oprs[1] = + solution_with_opr_count->data[solution_with_opr_count->size - 1]; + + free_vector(solution); + free_vector(solution_with_opr_count); + } + + // LU-BSUBST + { + Array_double *solution_with_opr_count = solve_matrix_lu_bsubst(m, b); + Array_double *solution = slice_element(solution_with_opr_count, + solution_with_opr_count->size - 1); + + for (size_t i = 0; i < solution->size; i++) + errs[2] += fabs(solution->data[i] - 1.0); + + oprs[2] = + solution_with_opr_count->data[solution_with_opr_count->size - 1]; + + free_vector(solution); + free_vector(solution_with_opr_count); + } + free_matrix(m); + free_vector(b_1); + free_vector(b); + + printf("| %zu | %f | %f | %f | %f | %f | %f | \n", i, oprs[0], errs[0], + oprs[1], errs[1], oprs[2], errs[2]); + } +} +#+END_SRC diff --git a/Homework/math4610/homeworks/hw-8.pdf b/Homework/math4610/homeworks/hw-8.pdf Binary files differnew file mode 100644 index 0000000..c14bb2e --- /dev/null +++ b/Homework/math4610/homeworks/hw-8.pdf diff --git a/Homework/math4610/homeworks/hw-8.tex b/Homework/math4610/homeworks/hw-8.tex new file mode 100644 index 0000000..9071f5b --- /dev/null +++ b/Homework/math4610/homeworks/hw-8.tex @@ -0,0 +1,344 @@ +% Created 2023-12-09 Sat 22:06 +% Intended LaTeX compiler: pdflatex +\documentclass[11pt]{article} +\usepackage[utf8]{inputenc} +\usepackage[T1]{fontenc} +\usepackage{graphicx} +\usepackage{longtable} +\usepackage{wrapfig} +\usepackage{rotating} +\usepackage[normalem]{ulem} +\usepackage{amsmath} +\usepackage{amssymb} +\usepackage{capt-of} +\usepackage{hyperref} +\notindent \notag \usepackage{amsmath} \usepackage[a4paper,margin=1in,portrait]{geometry} +\author{Elizabeth Hunt} +\date{\today} +\title{Homework 8} +\hypersetup{ + pdfauthor={Elizabeth Hunt}, + pdftitle={Homework 8}, + pdfkeywords={}, + pdfsubject={}, + pdfcreator={Emacs 28.2 (Org mode 9.7-pre)}, + pdflang={English}} +\begin{document} + +\maketitle +\setlength\parindent{0pt} + +\section{Question One} +\label{sec:org800c743} +See \texttt{UTEST(jacobi, solve\_jacobi)} in \texttt{test/jacobi.t.c} and the entry +\texttt{Jacobi / Gauss-Siedel -> solve\_jacobi} in the LIZFCM API documentation. +\section{Question Two} +\label{sec:org6121bef} +We cannot just perform the Jacobi algorithm on a Leslie matrix since +it is obviously not diagonally dominant - which is a requirement. It is +certainly not always the case, but, if a Leslie matrix \(L\) is invertible, we can +first perform gaussian elimination on \(L\) augmented with \(n_{k+1}\) +to obtain \(n_k\) with the Jacobi method. See \texttt{UTEST(jacobi, leslie\_solve)} +in \texttt{test/jacobi.t.c} for an example wherein this method is tested on a Leslie +matrix to recompute a given initial population distribution. + +In terms of accuracy, an LU factorization and back substitution approach will +always be as correct as possible within the limits of computation; it's a +direct solution method. It's simply the nature of the Jacobi algorithm being +a convergent solution that determines its accuracy. + +LU factorization also performs in order \(O(n^3)\) runtime for an \(n \times n\) +matrix, whereas the Jacobi algorithm runs in order \(O(k n^2) = O(n^2)\) on average +but with the con that \(k\) is given by some function on both the convergence criteria and the number of +nonzero entries in the matrix - which might end up worse in some cases than the LU decomp approach. + +\section{Question Three} +\label{sec:org11282e6} +See \texttt{UTEST(jacobi, gauss\_siedel\_solve)} in \texttt{test/jacobi.t.c} which runs the same +unit test as \texttt{UTEST(jacobi, solve\_jacobi)} but using the +\texttt{Jacobi / Gauss-Siedel -> gauss\_siedel\_solve} method as documented in the LIZFCM API reference. + +\section{Question Four, Five} +\label{sec:org22b52a9} +We produce the following operation counts (by hackily adding the operation count as the last element +to the solution vector) and errors - the sum of each vector elements' absolute value away from 1.0 +using the proceeding patch and unit test. + +\begin{center} +\begin{tabular}{rrrrrrr} +N & JAC opr & JAC err & GS opr & GS err & LU opr & LU err\\[0pt] +5 & 1622 & 0.001244 & 577 & 0.000098 & 430 & 0.000000\\[0pt] +6 & 2812 & 0.001205 & 775 & 0.000080 & 681 & 0.000000\\[0pt] +7 & 5396 & 0.001187 & 860 & 0.000178 & 1015 & 0.000000\\[0pt] +8 & 5618 & 0.001468 & 1255 & 0.000121 & 1444 & 0.000000\\[0pt] +9 & 7534 & 0.001638 & 1754 & 0.000091 & 1980 & 0.000000\\[0pt] +10 & 10342 & 0.001425 & 1847 & 0.000435 & 2635 & 0.000000\\[0pt] +11 & 12870 & 0.001595 & 2185 & 0.000368 & 3421 & 0.000000\\[0pt] +12 & 17511 & 0.001860 & 2912 & 0.000322 & 4350 & 0.000000\\[0pt] +13 & 16226 & 0.001631 & 3362 & 0.000270 & 5434 & 0.000000\\[0pt] +14 & 34333 & 0.001976 & 3844 & 0.000121 & 6685 & 0.000000\\[0pt] +15 & 38474 & 0.001922 & 4358 & 0.000311 & 8115 & 0.000000\\[0pt] +16 & 40405 & 0.002061 & 4904 & 0.000204 & 9736 & 0.000000\\[0pt] +17 & 58518 & 0.002125 & 5482 & 0.000311 & 11560 & 0.000000\\[0pt] +18 & 68079 & 0.002114 & 6092 & 0.000279 & 13599 & 0.000000\\[0pt] +19 & 95802 & 0.002159 & 6734 & 0.000335 & 15865 & 0.000000\\[0pt] +20 & 85696 & 0.002141 & 7408 & 0.000289 & 18370 & 0.000000\\[0pt] +21 & 89026 & 0.002316 & 8114 & 0.000393 & 21126 & 0.000000\\[0pt] +22 & 101537 & 0.002344 & 8852 & 0.000414 & 24145 & 0.000000\\[0pt] +23 & 148040 & 0.002323 & 9622 & 0.000230 & 27439 & 0.000000\\[0pt] +24 & 137605 & 0.002348 & 10424 & 0.000213 & 31020 & 0.000000\\[0pt] +25 & 169374 & 0.002409 & 11258 & 0.000894 & 34900 & 0.000000\\[0pt] +26 & 215166 & 0.002502 & 12124 & 0.000564 & 39091 & 0.000000\\[0pt] +27 & 175476 & 0.002616 & 13022 & 0.000535 & 43605 & 0.000000\\[0pt] +28 & 268454 & 0.002651 & 13952 & 0.000690 & 48454 & 0.000000\\[0pt] +29 & 267034 & 0.002697 & 14914 & 0.000675 & 53650 & 0.000000\\[0pt] +30 & 277193 & 0.002686 & 15908 & 0.000542 & 59205 & 0.000000\\[0pt] +31 & 336792 & 0.002736 & 16934 & 0.000390 & 65131 & 0.000000\\[0pt] +32 & 293958 & 0.002741 & 17992 & 0.000660 & 71440 & 0.000000\\[0pt] +33 & 323638 & 0.002893 & 19082 & 0.001072 & 78144 & 0.000000\\[0pt] +34 & 375104 & 0.003001 & 20204 & 0.001018 & 85255 & 0.000000\\[0pt] +35 & 436092 & 0.003004 & 21358 & 0.000912 & 92785 & 0.000000\\[0pt] +36 & 538143 & 0.003005 & 22544 & 0.000954 & 100746 & 0.000000\\[0pt] +37 & 511886 & 0.003029 & 23762 & 0.000462 & 109150 & 0.000000\\[0pt] +38 & 551332 & 0.003070 & 25012 & 0.000996 & 118009 & 0.000000\\[0pt] +39 & 592750 & 0.003110 & 26294 & 0.000989 & 127335 & 0.000000\\[0pt] +40 & 704208 & 0.003165 & 27608 & 0.000583 & 137140 & 0.000000\\[0pt] +\end{tabular} +\end{center} + +\begin{verbatim} +diff --git a/src/matrix.c b/src/matrix.c +index 901a426..af5529f 100644 +--- a/src/matrix.c ++++ b/src/matrix.c +@@ -144,20 +144,54 @@ Array_double *solve_matrix_lu_bsubst(Matrix_double *m, Array_double *b) { + assert(b->size == m->rows); + assert(m->rows == m->cols); + ++ double opr = 0; ++ ++ opr += b->size; + Array_double *x = copy_vector(b); ++ ++ size_t n = m->rows; ++ opr += n * n; // (u copy) ++ opr += n * n; // l_empty ++ opr += n * n + n; // copy + put_identity_diagonal ++ opr += n; // pivot check ++ opr += m->cols; ++ for (size_t x = 0; x < m->cols; x++) { ++ opr += (m->rows - (x + 1)); ++ for (size_t y = x + 1; y < m->rows; y++) { ++ opr += 1; ++ opr += 2; // -factor ++ opr += 4 * n; // scale, add_v, free_vector ++ opr += 1; // -factor ++ } ++ } ++ opr += n; + Matrix_double **u_l = lu_decomp(m); ++ + Matrix_double *u = u_l[0]; + Matrix_double *l = u_l[1]; + ++ opr += n; ++ for (int64_t row = n - 1; row >= 0; row--) { ++ opr += 2 * (n - row); ++ opr += 1; ++ } + Array_double *b_fsub = fsubst(l, b); ++ ++ opr += n; ++ for (size_t x = 0; x < n; x++) { ++ opr += 2 * (x + 1); ++ opr += 1; // /= l->data[row]->data[row] ++ } + x = bsubst(u, b_fsub); +- free_vector(b_fsub); + ++ free_vector(b_fsub); + free_matrix(u); + free_matrix(l); + free(u_l); + +- return x; ++ Array_double *copy = add_element(x, opr); ++ free_vector(x); ++ return copy; + } + + Matrix_double *gaussian_elimination(Matrix_double *m) { +@@ -231,18 +265,36 @@ Array_double *jacobi_solve(Matrix_double *m, Array_double *b, + assert(b->size == m->cols); + size_t iter = max_iterations; + ++ double opr = 0; ++ ++ opr += 2 * b->size; // to initialize two vectors with the same dim of b twice + Array_double *x_k = InitArrayWithSize(double, b->size, 0.0); + Array_double *x_k_1 = + InitArrayWithSize(double, b->size, rand_from(0.1, 10.0)); + ++ // add since these wouldn't be accounter for after the loop ++ opr += 1; // iter decrement ++ opr += ++ 3 * x_k_1->size; // 1 to perform x_k_1, x_k and 2 to perform ||x_k_1||_2 + while ((--iter) > 0 && l2_distance(x_k_1, x_k) > l2_convergence_tolerance) { ++ opr += 1; // iter decrement ++ opr += ++ 3 * x_k_1->size; // 1 to perform x_k_1, x_k and 2 to perform ||x_k_1||_2 ++ ++ opr += m->rows; // row for add oprs + for (size_t i = 0; i < m->rows; i++) { + double delta = 0.0; ++ ++ opr += m->cols; + for (size_t j = 0; j < m->cols; j++) { + if (i == j) + continue; ++ ++ opr += 1; + delta += m->data[i]->data[j] * x_k->data[j]; + } ++ ++ opr += 2; + x_k_1->data[i] = (b->data[i] - delta) / m->data[i]->data[i]; + } + +@@ -251,8 +303,9 @@ Array_double *jacobi_solve(Matrix_double *m, Array_double *b, + x_k_1 = tmp; + } + +- free_vector(x_k); +- return x_k_1; ++ Array_double *copy = add_element(x_k_1, opr); ++ free_vector(x_k_1); ++ return copy; + } + + Array_double *gauss_siedel_solve(Matrix_double *m, Array_double *b, +@@ -262,30 +315,48 @@ Array_double *gauss_siedel_solve(Matrix_double *m, Array_double *b, + assert(b->size == m->cols); + size_t iter = max_iterations; + ++ double opr = 0; ++ ++ opr += 2 * b->size; // to initialize two vectors with the same dim of b twice + Array_double *x_k = InitArrayWithSize(double, b->size, 0.0); + Array_double *x_k_1 = + InitArrayWithSize(double, b->size, rand_from(0.1, 10.0)); + + while ((--iter) > 0) { ++ opr += 1; // iter decrement ++ ++ opr += x_k->size; // copy oprs + for (size_t i = 0; i < x_k->size; i++) + x_k->data[i] = x_k_1->data[i]; + ++ opr += m->rows; // row for add oprs + for (size_t i = 0; i < m->rows; i++) { + double delta = 0.0; ++ ++ opr += m->cols; + for (size_t j = 0; j < m->cols; j++) { + if (i == j) + continue; ++ ++ opr += 1; + delta += m->data[i]->data[j] * x_k_1->data[j]; + } ++ ++ opr += 2; + x_k_1->data[i] = (b->data[i] - delta) / m->data[i]->data[i]; + } + ++ opr += ++ 3 * x_k_1->size; // 1 to perform x_k_1, x_k and 2 to perform ||x_k_1||_2 + if (l2_distance(x_k_1, x_k) <= l2_convergence_tolerance) + break; + } + + free_vector(x_k); +- return x_k_1; ++ ++ Array_double *copy = add_element(x_k_1, opr); ++ free_vector(x_k_1); ++ return copy; + } +\end{verbatim} + + +And this unit test: +\begin{verbatim} +UTEST(hw_8, p4_5) { + printf("| N | JAC opr | JAC err | GS opr | GS err | LU opr | LU err | \n"); + + for (size_t i = 5; i < 100; i++) { + Matrix_double *m = generate_ddm(i); + double oprs[3] = {0.0, 0.0, 0.0}; + double errs[3] = {0.0, 0.0, 0.0}; + + Array_double *b_1 = InitArrayWithSize(double, m->rows, 1.0); + Array_double *b = m_dot_v(m, b_1); + double tolerance = 0.001; + size_t max_iter = 400; + + // JACOBI + { + Array_double *solution_with_opr_count = + jacobi_solve(m, b, tolerance, max_iter); + Array_double *solution = slice_element(solution_with_opr_count, + solution_with_opr_count->size - 1); + + for (size_t i = 0; i < solution->size; i++) + errs[0] += fabs(solution->data[i] - 1.0); + + oprs[0] = + solution_with_opr_count->data[solution_with_opr_count->size - 1]; + + free_vector(solution); + free_vector(solution_with_opr_count); + } + + // GAUSS-SIEDEL + { + Array_double *solution_with_opr_count = + gauss_siedel_solve(m, b, tolerance, max_iter); + Array_double *solution = slice_element(solution_with_opr_count, + solution_with_opr_count->size - 1); + + for (size_t i = 0; i < solution->size; i++) + errs[1] += fabs(solution->data[i] - 1.0); + + oprs[1] = + solution_with_opr_count->data[solution_with_opr_count->size - 1]; + + free_vector(solution); + free_vector(solution_with_opr_count); + } + + // LU-BSUBST + { + Array_double *solution_with_opr_count = solve_matrix_lu_bsubst(m, b); + Array_double *solution = slice_element(solution_with_opr_count, + solution_with_opr_count->size - 1); + + for (size_t i = 0; i < solution->size; i++) + errs[2] += fabs(solution->data[i] - 1.0); + + oprs[2] = + solution_with_opr_count->data[solution_with_opr_count->size - 1]; + + free_vector(solution); + free_vector(solution_with_opr_count); + } + free_matrix(m); + free_vector(b_1); + free_vector(b); + + printf("| %zu | %f | %f | %f | %f | %f | %f | \n", i, oprs[0], errs[0], + oprs[1], errs[1], oprs[2], errs[2]); + } +} +\end{verbatim} +\end{document}
\ No newline at end of file diff --git a/Homework/math4610/homeworks/hw-9.org b/Homework/math4610/homeworks/hw-9.org new file mode 100644 index 0000000..de58d2a --- /dev/null +++ b/Homework/math4610/homeworks/hw-9.org @@ -0,0 +1,222 @@ +#+TITLE: Homework 9 +#+AUTHOR: Elizabeth Hunt +#+LATEX_HEADER: \notindent \notag \usepackage{amsmath} \usepackage[a4paper,margin=1in,portrait]{geometry} +#+LATEX: \setlength\parindent{0pt} +#+OPTIONS: toc:nil + +* Question One + +With a ~matrix_dimension~ set to 700, I consistently see about a 3x improvement in performance on my +10-thread machine. The serial implementation gives an average ~0.189s~ total runtime, while the below +parallel implementation runs in about ~0.066s~ after the cpu cache has filled on the first run. + +#+BEGIN_SRC c +#include <math.h> +#include <omp.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> + +#define matrix_dimension 700 + +int n = matrix_dimension; +float sum; + +int main() { + float A[n][n]; + float x0[n]; + float b[n]; + float x1[n]; + float res[n]; + + srand((unsigned int)(time(NULL))); + + // not worth parallellization - rand() is not thread-safe + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + A[i][j] = ((float)rand() / (float)(RAND_MAX) * 5.0); + } + x0[i] = ((float)rand() / (float)(RAND_MAX) * 5.0); + } + +#pragma omp parallel for private(sum) + for (int i = 0; i < n; i++) { + sum = 0.0; + for (int j = 0; j < n; j++) { + sum += fabs(A[i][j]); + } + A[i][i] += sum; + } + +#pragma omp parallel for private(sum) + for (int i = 0; i < n; i++) { + sum = 0.0; + for (int j = 0; j < n; j++) { + sum += A[i][j]; + } + b[i] = sum; + } + + float tol = 0.0001; + float error = 10.0 * tol; + int maxiter = 100; + int iter = 0; + + while (error > tol && iter < maxiter) { +#pragma omp parallel for + for (int i = 0; i < n; i++) { + float temp_sum = b[i]; + for (int j = 0; j < n; j++) { + temp_sum -= A[i][j] * x0[j]; + } + res[i] = temp_sum; + x1[i] = x0[i] + res[i] / A[i][i]; + } + + sum = 0.0; +#pragma omp parallel for reduction(+ : sum) + for (int i = 0; i < n; i++) { + float val = x1[i] - x0[i]; + sum += val * val; + } + error = sqrt(sum); + +#pragma omp parallel for + for (int i = 0; i < n; i++) { + x0[i] = x1[i]; + } + + iter++; + } + + for (int i = 0; i < n; i++) + printf("x[%d] = %6f \t res[%d] = %6f\n", i, x1[i], i, res[i]); + + return 0; +} + +#+END_SRC + +* Question Two + +I only see lowerings in performance (likely due to overhead) on my machine using OpenMP until +~matrix_dimension~ becomes quite large, about ~300~ in testing. At ~matrix_dimension=1000~, I see another +about 3x improvement in total runtime (including initialization & I/O which was untouched, so, even further +improvements could be made) on my 10-thread machine; from around ~0.174~ seconds to ~.052~. + +#+BEGIN_SRC c + #include <math.h> + #include <stdio.h> + #include <stdlib.h> + #include <time.h> + + #ifdef _OPENMP + #include <omp.h> + #else + #define omp_get_num_threads() 0 + #define omp_set_num_threads(int) 0 + #define omp_get_thread_num() 0 + #endif + + #define matrix_dimension 1000 + + int n = matrix_dimension; + float ynrm; + + int main() { + float A[n][n]; + float v0[n]; + float v1[n]; + float y[n]; + // + // create a matrix + // + // not worth parallellization - rand() is not thread-safe + srand((unsigned int)(time(NULL))); + float a = 5.0; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + A[i][j] = ((float)rand() / (float)(RAND_MAX)*a); + } + v0[i] = ((float)rand() / (float)(RAND_MAX)*a); + } + // + // modify the diagonal entries for diagonal dominance + // -------------------------------------------------- + // + for (int i = 0; i < n; i++) { + float sum = 0.0; + for (int j = 0; j < n; j++) { + sum = sum + fabs(A[i][j]); + } + A[i][i] = A[i][i] + sum; + } + // + // generate a vector of ones + // ------------------------- + // + for (int j = 0; j < n; j++) { + v0[j] = 1.0; + } + // + // power iteration test + // -------------------- + // + float tol = 0.0000001; + float error = 10.0 * tol; + float lam1, lam0; + int maxiter = 100; + int iter = 0; + + while (error > tol && iter < maxiter) { + #pragma omp parallel for + for (int i = 0; i < n; i++) { + y[i] = 0; + for (int j = 0; j < n; j++) { + y[i] = y[i] + A[i][j] * v0[j]; + } + } + + ynrm = 0.0; + #pragma omp parallel for reduction(+ : ynrm) + for (int i = 0; i < n; i++) { + ynrm += y[i] * y[i]; + } + ynrm = sqrt(ynrm); + + #pragma omp parallel for + for (int i = 0; i < n; i++) { + v1[i] = y[i] / ynrm; + } + + #pragma omp parallel for + for (int i = 0; i < n; i++) { + y[i] = 0.0; + for (int j = 0; j < n; j++) { + y[i] += A[i][j] * v1[j]; + } + } + + lam1 = 0.0; + #pragma omp parallel for reduction(+ : lam1) + for (int i = 0; i < n; i++) { + lam1 += v1[i] * y[i]; + } + + error = fabs(lam1 - lam0); + lam0 = lam1; + + #pragma omp parallel for + for (int i = 0; i < n; i++) { + v0[i] = v1[i]; + } + + iter++; + } + + printf("in %d iterations, eigenvalue = %f\n", iter, lam1); + } +#+END_SRC + +* Question Three +[[https://static.simponic.xyz/lizfcm.pdf]] diff --git a/Homework/math4610/homeworks/hw-9.pdf b/Homework/math4610/homeworks/hw-9.pdf Binary files differnew file mode 100644 index 0000000..4753a3b --- /dev/null +++ b/Homework/math4610/homeworks/hw-9.pdf diff --git a/Homework/math4610/homeworks/hw-9.tex b/Homework/math4610/homeworks/hw-9.tex new file mode 100644 index 0000000..9d5693a --- /dev/null +++ b/Homework/math4610/homeworks/hw-9.tex @@ -0,0 +1,250 @@ +% Created 2023-12-11 Mon 19:24 +% Intended LaTeX compiler: pdflatex +\documentclass[11pt]{article} +\usepackage[utf8]{inputenc} +\usepackage[T1]{fontenc} +\usepackage{graphicx} +\usepackage{longtable} +\usepackage{wrapfig} +\usepackage{rotating} +\usepackage[normalem]{ulem} +\usepackage{amsmath} +\usepackage{amssymb} +\usepackage{capt-of} +\usepackage{hyperref} +\notindent \notag \usepackage{amsmath} \usepackage[a4paper,margin=1in,portrait]{geometry} +\author{Elizabeth Hunt} +\date{\today} +\title{Homework 9} +\hypersetup{ + pdfauthor={Elizabeth Hunt}, + pdftitle={Homework 9}, + pdfkeywords={}, + pdfsubject={}, + pdfcreator={Emacs 28.2 (Org mode 9.7-pre)}, + pdflang={English}} +\begin{document} + +\maketitle +\setlength\parindent{0pt} + +\section{Question One} +\label{sec:org69bed2d} + +With a \texttt{matrix\_dimension} set to 700, I consistently see about a 3x improvement in performance on my +10-thread machine. The serial implementation gives an average \texttt{0.189s} total runtime, while the below +parallel implementation runs in about \texttt{0.066s} after the cpu cache has filled on the first run. + +\begin{verbatim} +#include <math.h> +#include <omp.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> + +#define matrix_dimension 700 + +int n = matrix_dimension; +float sum; + +int main() { + float A[n][n]; + float x0[n]; + float b[n]; + float x1[n]; + float res[n]; + + srand((unsigned int)(time(NULL))); + + // not worth parallellization - rand() is not thread-safe + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + A[i][j] = ((float)rand() / (float)(RAND_MAX) * 5.0); + } + x0[i] = ((float)rand() / (float)(RAND_MAX) * 5.0); + } + +#pragma omp parallel for private(sum) + for (int i = 0; i < n; i++) { + sum = 0.0; + for (int j = 0; j < n; j++) { + sum += fabs(A[i][j]); + } + A[i][i] += sum; + } + +#pragma omp parallel for private(sum) + for (int i = 0; i < n; i++) { + sum = 0.0; + for (int j = 0; j < n; j++) { + sum += A[i][j]; + } + b[i] = sum; + } + + float tol = 0.0001; + float error = 10.0 * tol; + int maxiter = 100; + int iter = 0; + + while (error > tol && iter < maxiter) { +#pragma omp parallel for + for (int i = 0; i < n; i++) { + float temp_sum = b[i]; + for (int j = 0; j < n; j++) { + temp_sum -= A[i][j] * x0[j]; + } + res[i] = temp_sum; + x1[i] = x0[i] + res[i] / A[i][i]; + } + + sum = 0.0; +#pragma omp parallel for reduction(+ : sum) + for (int i = 0; i < n; i++) { + float val = x1[i] - x0[i]; + sum += val * val; + } + error = sqrt(sum); + +#pragma omp parallel for + for (int i = 0; i < n; i++) { + x0[i] = x1[i]; + } + + iter++; + } + + for (int i = 0; i < n; i++) + printf("x[%d] = %6f \t res[%d] = %6f\n", i, x1[i], i, res[i]); + + return 0; +} + +\end{verbatim} + +\section{Question Two} +\label{sec:orgbeace25} + +I only see lowerings in performance (likely due to overhead) on my machine using OpenMP until +\texttt{matrix\_dimension} becomes quite large, about \texttt{300} in testing. At \texttt{matrix\_dimension=1000}, I see another +about 3x improvement in total runtime (including initialization \& I/O which was untouched, so, even further +improvements could be made) on my 10-thread machine; from around \texttt{0.174} seconds to \texttt{.052}. + +\begin{verbatim} +#include <math.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> + +#ifdef _OPENMP +#include <omp.h> +#else +#define omp_get_num_threads() 0 +#define omp_set_num_threads(int) 0 +#define omp_get_thread_num() 0 +#endif + +#define matrix_dimension 1000 + +int n = matrix_dimension; +float ynrm; + +int main() { + float A[n][n]; + float v0[n]; + float v1[n]; + float y[n]; + // + // create a matrix + // + // not worth parallellization - rand() is not thread-safe + srand((unsigned int)(time(NULL))); + float a = 5.0; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + A[i][j] = ((float)rand() / (float)(RAND_MAX)*a); + } + v0[i] = ((float)rand() / (float)(RAND_MAX)*a); + } + // + // modify the diagonal entries for diagonal dominance + // -------------------------------------------------- + // + for (int i = 0; i < n; i++) { + float sum = 0.0; + for (int j = 0; j < n; j++) { + sum = sum + fabs(A[i][j]); + } + A[i][i] = A[i][i] + sum; + } + // + // generate a vector of ones + // ------------------------- + // + for (int j = 0; j < n; j++) { + v0[j] = 1.0; + } + // + // power iteration test + // -------------------- + // + float tol = 0.0000001; + float error = 10.0 * tol; + float lam1, lam0; + int maxiter = 100; + int iter = 0; + + while (error > tol && iter < maxiter) { +#pragma omp parallel for + for (int i = 0; i < n; i++) { + y[i] = 0; + for (int j = 0; j < n; j++) { + y[i] = y[i] + A[i][j] * v0[j]; + } + } + + ynrm = 0.0; +#pragma omp parallel for reduction(+ : ynrm) + for (int i = 0; i < n; i++) { + ynrm += y[i] * y[i]; + } + ynrm = sqrt(ynrm); + +#pragma omp parallel for + for (int i = 0; i < n; i++) { + v1[i] = y[i] / ynrm; + } + +#pragma omp parallel for + for (int i = 0; i < n; i++) { + y[i] = 0.0; + for (int j = 0; j < n; j++) { + y[i] += A[i][j] * v1[j]; + } + } + + lam1 = 0.0; +#pragma omp parallel for reduction(+ : lam1) + for (int i = 0; i < n; i++) { + lam1 += v1[i] * y[i]; + } + + error = fabs(lam1 - lam0); + lam0 = lam1; + +#pragma omp parallel for + for (int i = 0; i < n; i++) { + v0[i] = v1[i]; + } + + iter++; + } + + printf("in %d iterations, eigenvalue = %f\n", iter, lam1); +} +\end{verbatim} + +\section{Question Three} +\label{sec:org33439e0} +\url{https://static.simponic.xyz/lizfcm.pdf} +\end{document}
\ No newline at end of file diff --git a/Homework/math4610/homeworks/hw_6_p_8 b/Homework/math4610/homeworks/hw_6_p_8 Binary files differnew file mode 100644 index 0000000..46b58a2 --- /dev/null +++ b/Homework/math4610/homeworks/hw_6_p_8 diff --git a/Homework/math4610/homeworks/hw_6_p_8.c b/Homework/math4610/homeworks/hw_6_p_8.c new file mode 100644 index 0000000..56f199f --- /dev/null +++ b/Homework/math4610/homeworks/hw_6_p_8.c @@ -0,0 +1,89 @@ +// compile & test w/ +// \--> gcc -I../inc/ -Wall hw_6_p_8.c ../lib/lizfcm.a -lm -o hw_6_p_8 +// \--> ./hw_6_p_8 + +#include "lizfcm.h" +#include <math.h> +#include <stdio.h> + +double a(double t) { + double alpha = 0.1; + double beta = 0.001; + double p_0 = 2; + double p_infty = 29.75; + + return p_0 * exp(t * (alpha - beta)) - p_infty; +} + +double b(double t) { + double alpha = 0.1; + double beta = 0.001; + double p_0 = 2; + double p_infty = 115.35; + + return p_0 * exp(t * (alpha - beta)) - p_infty; +} + +double c(double t) { + double alpha = 0.1; + double beta = 0.0001; + double p_0 = 2; + double p_infty = 115.35; + + return p_0 * exp(t * (alpha - beta)) - p_infty; +} + +double d(double t) { + double alpha = 0.01; + double beta = 0.001; + double p_0 = 2; + double p_infty = 155.346; + + return p_0 * exp(t * (alpha - beta)) - p_infty; +} + +double e(double t) { + double alpha = 0.1; + double beta = 0.01; + double p_0 = 100; + double p_infty = 155.346; + + return p_0 * exp(t * (alpha - beta)) - p_infty; +} + +int main() { + uint64_t max_iterations = 1000; + double tolerance = 0.0000001; + + Array_double *ivt_range = find_ivt_range(&a, -5.0, 3.0, 1000); + double approx_a = fixed_point_secant_bisection_method( + &a, ivt_range->data[0], ivt_range->data[1], tolerance, max_iterations); + + free_vector(ivt_range); + ivt_range = find_ivt_range(&b, -5.0, 3.0, 1000); + double approx_b = fixed_point_secant_bisection_method( + &b, ivt_range->data[0], ivt_range->data[1], tolerance, max_iterations); + + free_vector(ivt_range); + ivt_range = find_ivt_range(&c, -5.0, 3.0, 1000); + double approx_c = fixed_point_secant_bisection_method( + &c, ivt_range->data[0], ivt_range->data[1], tolerance, max_iterations); + + free_vector(ivt_range); + ivt_range = find_ivt_range(&d, -5.0, 3.0, 1000); + double approx_d = fixed_point_secant_bisection_method( + &d, ivt_range->data[0], ivt_range->data[1], tolerance, max_iterations); + + free_vector(ivt_range); + ivt_range = find_ivt_range(&e, -5.0, 3.0, 1000); + double approx_e = fixed_point_secant_bisection_method( + &e, ivt_range->data[0], ivt_range->data[1], tolerance, max_iterations); + + printf("a ~ %f; P(%f) - P_infty = %f\n", approx_a, approx_a, a(approx_a)); + printf("b ~ %f; P(%f) - P_infty = %f\n", approx_b, approx_b, b(approx_b)); + printf("c ~ %f; P(%f) - P_infty = %f\n", approx_c, approx_c, c(approx_c)); + printf("d ~ %f; P(%f) - P_infty = %f\n", approx_d, approx_d, d(approx_d)); + printf("e ~ %f; P(%f) - P_infty = %f\n", approx_e, approx_e, e(approx_e)); + + return 0; +} diff --git a/Homework/math4610/homeworks/img/make_run.png b/Homework/math4610/homeworks/img/make_run.png Binary files differnew file mode 100644 index 0000000..f20015e --- /dev/null +++ b/Homework/math4610/homeworks/img/make_run.png diff --git a/Homework/math4610/homeworks/img/test_routine_1.png b/Homework/math4610/homeworks/img/test_routine_1.png Binary files differnew file mode 100644 index 0000000..57ce59f --- /dev/null +++ b/Homework/math4610/homeworks/img/test_routine_1.png diff --git a/Homework/math4610/homeworks/img/test_routine_2.png b/Homework/math4610/homeworks/img/test_routine_2.png Binary files differnew file mode 100644 index 0000000..b116620 --- /dev/null +++ b/Homework/math4610/homeworks/img/test_routine_2.png diff --git a/Homework/math4610/homeworks/virtualization/hw1.pdf b/Homework/math4610/homeworks/virtualization/hw1.pdf Binary files differnew file mode 100644 index 0000000..00d84a4 --- /dev/null +++ b/Homework/math4610/homeworks/virtualization/hw1.pdf diff --git a/Homework/math4610/homeworks/virtualization/img/htop.png b/Homework/math4610/homeworks/virtualization/img/htop.png Binary files differnew file mode 100644 index 0000000..880befa --- /dev/null +++ b/Homework/math4610/homeworks/virtualization/img/htop.png diff --git a/Homework/math4610/homeworks/virtualization/img/no_virtualization.png b/Homework/math4610/homeworks/virtualization/img/no_virtualization.png Binary files differnew file mode 100644 index 0000000..2322456 --- /dev/null +++ b/Homework/math4610/homeworks/virtualization/img/no_virtualization.png diff --git a/Homework/math4610/homeworks/virtualization/virtual_machines.md b/Homework/math4610/homeworks/virtualization/virtual_machines.md new file mode 100644 index 0000000..c6d3e12 --- /dev/null +++ b/Homework/math4610/homeworks/virtualization/virtual_machines.md @@ -0,0 +1,41 @@ +* Elizabeth Hunt (A02364151), MATH 4610 + +## Virtual Machines + +**Question 1** + +Run the Linux OS as a virtual machine, or run the application in a containerized Linux environment (which +is the same abstraction). + +**Question 2** + +A native system virtual machine has dedicated hardware to run the hypervisor, while a hosted system +virtual machine runs a hypervisor as a process in the operating system. + +**Question 3** + +A virtual machine hosts an entire operating system and requires users to perform configuration if they +want to run an application, whereas a Virtual Appliance is built to provide an easy plug-and-play virtual +machine image built to run some specific software stack. + +**Question 4** + +In a large application sense, containerizing services into their own virtual machines allows for easier +replication, scaling, and networking. Instead of running several smaller servers, one large server can +host several applications in parallel. This provides a good seperation of concern. And, if one service +goes down, the whole system does not go down with it. + +Locally, it can help in development when targeting another operating system. Virtual machines can be +used to verify builds without installing a whole other operating system. + +**Question 5** + +A virtual machine monitor is just another term for a hypervisor, so, see question 2. + +**Question 6** + +The three components of a virtual machine are: + +1. The host +2. The virtualization layer +3. The guest diff --git a/Homework/math4610/homeworks/virtualization/virtualization.md b/Homework/math4610/homeworks/virtualization/virtualization.md new file mode 100644 index 0000000..4d03637 --- /dev/null +++ b/Homework/math4610/homeworks/virtualization/virtualization.md @@ -0,0 +1,103 @@ +## Virtualization + +**Question 1** + +I use an Apple Silicon Mac which is based on the ARM architecture - so it's necessary to use +[Multipass](https://multipass.run/), as native virtualization is _not available to us_. + + + +**Question 2** + +One of the downsides of running a virtual machine, as opposed to a hosted virtual instance, is that local +resources are used. On a laptop especially, this increases power draw, draining the battery. Additionally, +the security of mind provided by "faster disaster recovery", as discussed in the article, is not as +necessary for consumer applications on personal machines as servers. Finally, virtual machines are +inherently slower in compute due to general overhead. + +**Question 3** + + + +**Question 4** + +In a large application sense, containerizing services into their own virtual machines allows for easier +replication, scaling, and networking. Instead of running several smaller servers, one large server can +host several applications in parallel. + +Locally, it can help in development when targeting another operating system. Virtual machines can be +used to verify builds without installing a whole other operating system. + +**Question 5** + +A native system virtual machine has dedicated hardware to run the hypervisor, while a hosted system +virtual machine runs a hypervisor as a process in the operating system. + +**Question 6** + +1. Easier networking between "servers" +2. Efficient resource use + +**Question 7** + +A Virtual Appliance is built to provide an easy plug-and-play virtual machine image built to run some +specific software stack. + +**Question 8** + +A Virtual Appliance would be desirable to eliminate maintenance and configuration overhead when running an +application. In my own experience, I've used a form of virtual appliances - "Docker Containers", to easily +spin up multiple versions of small services at work. + +**Question 9** What are 2 benefits of Virtualization? + +See question 6. + +**Question 10** + +See question 4. + +**Question 11** + +See question 8. + +**Question 12** What are the three main types of virtualization? + +1. Full virtualization +2. Para virtualization +3. OS-level virtualization + +**Question 13** What you should know about virtualization? + +How to create a virtual machine, and maintain it. + +**Question 14** What is the weakness of virtualization? + +Inherent overhead in all system operations. + +**Question 15** What are the six areas of virtualization? + +Source: [HiTechNectar](https://www.hitechnectar.com/blogs/virtualization-types) + +1. Application - run individual applications in a seperate environment than a host OS +2. Data - abstract exact location and formatting information away from retrieval of data +3. Desktop - hosts a desktop environment virtually on another machine (reminds me of mainframes). +4. Network - physical networking tools are abstracted into software resources +5. Server - division of a server into multiple guest operating systems +6. Storage - abstraction over multiple storage arrays into a single pool + +**Question 16** What is the biggest challenge in virtualization? + +Resource distribution is a big one; it's difficult to keep track of several resources on a host machine +and ensure a Virtual Machine accesses them correctly. + +**Question 17** What is the risk of using virtualization? + +The biggest risk of using virtualization is sandbox escape vulnerabilities. Although mostly research and +proof-of-concept, highly skilled engineers can theoretically craft exploits to escape the sandbox of the +VM and directly mess with the host operating system. + +**Question 18** + +When (question 17) is trusted; sandboxing. Virtualization should supply no access to resources within the +host operating system. diff --git a/Homework/math4610/inc/lizfcm.h b/Homework/math4610/inc/lizfcm.h new file mode 100644 index 0000000..1dcdb6b --- /dev/null +++ b/Homework/math4610/inc/lizfcm.h @@ -0,0 +1,100 @@ +#include "macros.h" +#include "types.h" + +#ifndef LIZFCM_H +#define LIZFCM_H + +extern float smaceps(); +extern double dmaceps(); + +extern double central_derivative_at(double (*f)(double), double a, double h); +extern double forward_derivative_at(double (*f)(double), double a, double h); +extern double backward_derivative_at(double (*f)(double), double a, double h); + +extern double sum_v(Array_double *v); +extern Array_double *scale_v(Array_double *v1, double m); +extern Array_double *add_v(Array_double *v1, Array_double *v2); +extern Array_double *minus_v(Array_double *v1, Array_double *v2); +extern double v_dot_v(Array_double *v1, Array_double *v2); +extern double l2_norm(Array_double *v); +extern double l1_norm(Array_double *v); +extern double linf_norm(Array_double *v); +extern double vector_distance(Array_double *v1, Array_double *v2, + double (*norm)(Array_double *)); +extern double l2_distance(Array_double *v1, Array_double *v2); +extern double l1_distance(Array_double *v1, Array_double *v2); +extern double linf_distance(Array_double *v1, Array_double *v2); +extern Array_double *copy_vector(Array_double *v1); +extern Array_double *add_element(Array_double *v, double x); +extern Array_double *slice_element(Array_double *v, size_t x); +extern void free_vector(Array_double *v); +extern void format_vector_into(Array_double *v, char *s); +extern int vector_equal(Array_double *a, Array_double *b); + +extern Matrix_double *put_identity_diagonal(Matrix_double *m); +extern Matrix_double **lu_decomp(Matrix_double *m); +extern Array_double *bsubst(Matrix_double *u, Array_double *b); +extern Array_double *fsubst(Matrix_double *l, Array_double *b); +extern Array_double *solve_matrix_lu_bsubst(Matrix_double *m, Array_double *b); +extern Matrix_double *gaussian_elimination(Matrix_double *m); +extern Array_double *solve_matrix_gaussian(Matrix_double *m, Array_double *b); +extern Array_double *m_dot_v(Matrix_double *m, Array_double *v); +extern Matrix_double *m_dot_m(Matrix_double *a, Matrix_double *b); +extern Matrix_double *transpose(Matrix_double *m); +extern Array_double *col_v(Matrix_double *m, size_t x); +extern Matrix_double *copy_matrix(Matrix_double *m); +extern Matrix_double *add_column(Matrix_double *m, Array_double *col); +extern Matrix_double *slice_column(Matrix_double *m, size_t col); +extern void free_matrix(Matrix_double *m); +extern void format_matrix_into(Matrix_double *m, char *s); +extern int matrix_equal(Matrix_double *a, Matrix_double *b); + +extern Line *least_squares_lin_reg(Array_double *x, Array_double *y); + +extern Array_double *find_ivt_range(double (*f)(double), double start_x, + double delta, size_t max_steps); +extern Array_double *bisect_find_root(double (*f)(double), double a, double b, + double tolerance, size_t max_iterations); +extern double bisect_find_root_with_error_assumption(double (*f)(double), + double a, double b, + double tolerance); +extern double fixed_point_iteration_method(double (*f)(double), + double (*g)(double), double x_0, + double tolerance, + size_t max_iterations); +extern double fixed_point_newton_method(double (*f)(double), + double (*fprime)(double), double x_0, + double tolerance, + size_t max_iterations); +extern double fixed_point_secant_method(double (*f)(double), double x_0, + double x_1, double tolerance, + size_t max_iterations); +extern double fixed_point_secant_bisection_method(double (*f)(double), + double x_0, double x_1, + double tolerance, + size_t max_iterations); + +extern double dominant_eigenvalue(Matrix_double *m, Array_double *v, + double tolerance, size_t max_iterations); +extern double shift_inverse_power_eigenvalue(Matrix_double *m, Array_double *v, + double shift, double tolerance, + size_t max_iterations); +extern double least_dominant_eigenvalue(Matrix_double *m, Array_double *v, + double tolerance, + size_t max_iterations); +extern Array_double *partition_find_eigenvalues(Matrix_double *m, + Matrix_double *guesses, + double tolerance, + size_t max_iterations); +extern Matrix_double *leslie_matrix(Array_double *age_class_surivor_ratio, + Array_double *age_class_offspring); + +extern double rand_from(double min, double max); + +extern Array_double *jacobi_solve(Matrix_double *m, Array_double *b, + double tolerance, size_t max_iterations); +extern Array_double *gauss_siedel_solve(Matrix_double *m, Array_double *b, + double l2_convergence_tolerance, + size_t max_iterations); + +#endif // LIZFCM_H diff --git a/Homework/math4610/inc/macros.h b/Homework/math4610/inc/macros.h new file mode 100644 index 0000000..d081869 --- /dev/null +++ b/Homework/math4610/inc/macros.h @@ -0,0 +1,58 @@ +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +#ifndef MACROS_H +#define MACROS_H + +#define DEFINE_ARRAY(TYPE) \ + typedef struct { \ + TYPE *data; \ + size_t size; \ + } Array_##TYPE + +#define DEFINE_MATRIX(TYPE) \ + typedef struct { \ + Array_##TYPE **data; \ + size_t cols; \ + size_t rows; \ + } Matrix_##TYPE + +#define InitArray(TYPE, ...) \ + ({ \ + TYPE temp[] = __VA_ARGS__; \ + Array_##TYPE *arr = malloc(sizeof(Array_##TYPE)); \ + arr->size = sizeof(temp) / sizeof(temp[0]); \ + arr->data = malloc(arr->size * sizeof(TYPE)); \ + memcpy(arr->data, temp, arr->size * sizeof(TYPE)); \ + arr; \ + }) + +#define InitArrayWithSize(TYPE, SIZE, INIT_VALUE) \ + ({ \ + Array_##TYPE *arr = malloc(sizeof(Array_##TYPE)); \ + arr->size = SIZE; \ + arr->data = malloc(arr->size * sizeof(TYPE)); \ + for (size_t i = 0; i < arr->size; i++) \ + arr->data[i] = INIT_VALUE; \ + arr; \ + }) + +#define InitMatrixWithSize(TYPE, ROWS, COLS, INIT_VALUE) \ + ({ \ + Matrix_##TYPE *matrix = malloc(sizeof(Matrix_##TYPE)); \ + matrix->rows = ROWS; \ + matrix->cols = COLS; \ + matrix->data = malloc(matrix->rows * sizeof(Array_##TYPE *)); \ + for (size_t y = 0; y < matrix->rows; y++) \ + matrix->data[y] = InitArrayWithSize(TYPE, COLS, INIT_VALUE); \ + matrix; \ + }) + +#define c_max(x, y) (((x) >= (y)) ? (x) : (y)) +#define c_min(x, y) (((x) <= (y)) ? (x) : (y)) + +#define true 1 +#define false 0 + +#endif // MACROS_H diff --git a/Homework/math4610/inc/types.h b/Homework/math4610/inc/types.h new file mode 100644 index 0000000..95ab96d --- /dev/null +++ b/Homework/math4610/inc/types.h @@ -0,0 +1,24 @@ +#include "macros.h" +#include <stdint.h> + +#ifndef TYPES_H +#define TYPES_H + +DEFINE_ARRAY(int); +DEFINE_ARRAY(uint32_t); +DEFINE_ARRAY(int32_t); +DEFINE_ARRAY(float); +DEFINE_ARRAY(double); + +DEFINE_MATRIX(int); +DEFINE_MATRIX(uint32_t); +DEFINE_MATRIX(int32_t); +DEFINE_MATRIX(float); +DEFINE_MATRIX(double); + +typedef struct Line { + double m; + double a; +} Line; + +#endif // TYPES_H diff --git a/Homework/math4610/lib/lizfcm.a b/Homework/math4610/lib/lizfcm.a Binary files differnew file mode 100644 index 0000000..d28d9aa --- /dev/null +++ b/Homework/math4610/lib/lizfcm.a diff --git a/Homework/math4610/notes/29-Nov.org b/Homework/math4610/notes/29-Nov.org new file mode 100644 index 0000000..a478ebf --- /dev/null +++ b/Homework/math4610/notes/29-Nov.org @@ -0,0 +1,20 @@ +Jacobi Iteration (cont.) + +x^{k+1} = D^{-1}(b - (L + U)x^k) + +{ + x^{k+1} = x^k + D^-1 r^k + r^{k} = b - Ax^k +} + +error: || x^{k+1} - x^k ||_2 +residual: || r^k ||_2 + +Gauss-Seidel Iteration: +A = (L + D + U) +\Rightarrow Ax = b + (D + U)x = b - Lx + x = (D + U)^-1 (b - Lx) + +x^{k+1} = (D+U)^{-1}(b - Lx^k) +(D + U)^{-1} x (bsubst) diff --git a/Homework/math4610/notes/4-Dec.org b/Homework/math4610/notes/4-Dec.org new file mode 100644 index 0000000..d148bc8 --- /dev/null +++ b/Homework/math4610/notes/4-Dec.org @@ -0,0 +1,2 @@ + + diff --git a/Homework/math4610/notes/Nov-27.org b/Homework/math4610/notes/Nov-27.org new file mode 100644 index 0000000..ae4ded0 --- /dev/null +++ b/Homework/math4610/notes/Nov-27.org @@ -0,0 +1,20 @@ +x^{k+1} = D^{-1}(b - (L + U) x^k) +x^{k + 1} \rightarrow Ax^k + + +#+BEGIN_SRC c + loop while (err > tol && iter < maxiter) { + for (int i = 0; i < n; i++) { + sum = b[i]; + for (int j = 0; j < i; j++) { + sum = sum - a[i][x] * x_0[i]; + } + for (int j = i; j < n; j++) { + sum = sum + a[i][j] * x_0[j]; + } + x_1[i] = sum / a[i][i]; + } + + err = 0.0; + } +#+END_SRC diff --git a/Homework/math4610/notes/Nov-3.org b/Homework/math4610/notes/Nov-3.org new file mode 100644 index 0000000..5a65d2a --- /dev/null +++ b/Homework/math4610/notes/Nov-3.org @@ -0,0 +1,62 @@ +* eigenvalues \rightarrow power method + +we iterate on the x_{k+1} = A x_k + +y = Av_0 +v_1 = \frac{1}{|| y ||} (y) +\lambda_0 = v_0^T A v_0 = v_0^T y + +Find the largest eigenvalue; + +#+BEGIN_SRC c + while (error > tol && iter < max_iter) { + v_1 = (1 / magnitude(y)) * y; + w = m_dot_v(a, v_1); + lambda_1 = v_dot_v(transpose(v_1), w); + error = abs(lambda_1 - lambda_0); + iter++; + lambda_0 = lambda_1; + y = v_1; + } + + return [lambda_1, error]; +#+END_SRC + +Find the smallest eigenvalue: + +** We know: +If \lambda_1 is the largest eigenvalue of $A$ then \frac{1}{\lambda_1} is the smallest eigenvalue of $A^{-1}$. + +If \lambda_n is the smallest eigenvalue of $A$ then \frac{1}{\lambda_n} is the largest eigenvalue of $A^{-1}$. +*** However, calculating $A^{-1}$ is inefficient +So, transform $w = A^{-1} v_1 \Rightarrow$ Solve $Aw = v_1$ with LU or GE (line 3 of above snippet). + +And, transform $y = A^{-1} v_0 \Rightarrow$ Solve $Ay = v_0$ with LU or GE. + +** Conclusions + +We have the means to compute the approximations of \lambda_1 and \lambda_n. + +(\lambda_1 \rightarrow power method) + +(\lambda_n \rightarrow inverse power method) + +* Eigenvalue Shifting + +If (\lambda, v) is an eigen pair, (v \neq 0) + +Av = \lambdav + +Thus for any \mu \in R + +(Av - \mu I v) = (A - \mu I)v = \lambda v - \mu I v + = (\lambda - \mu)v + \Rightarrow \lambda - \mu is an eigenvalue of (A - \mu I) + +(A - \mu I)v = (\lambda - \mu)v + +Idea is to choose \mu close to our eigenvalue. We can then inverse iterate to +construct an approximation of \lambda - \mu and then add \mu back to get \lambda. + +v_0 = a_1 v_1 + a_2 v_2 + \cdots + a_n v_n +A v_0 = a_1 (\lambda_1 v_1) + \cdots diff --git a/Homework/math4610/notes/Nov-6.org b/Homework/math4610/notes/Nov-6.org new file mode 100644 index 0000000..4a9562f --- /dev/null +++ b/Homework/math4610/notes/Nov-6.org @@ -0,0 +1,25 @@ +* Power Method +v_{k+1} = A v_k, k = 0,1,2 + +** Properties +1. \frac{A v_k}{||v_k||} \rightarrow v_1 +2. \frac{v_k^T A v_k}{v_k^T v_k} \rightarrow \lambda_1 +3. If \lambda is a n eigenvalue of A, then \frac{1}{\lambda} is an eigenvalue of A^-1 +4. Av = \lambda v + Av - \mu v = (\lambda-\mu)v = (A - \mu I)v +5. If \lambda is an eigenvalue of A, then \lambda - \mu is an eigenvalue of A \cdot \mu I + +** Shifting Eigenvalues +1. Partition [\lambda_n, \lambda_1] + + +* Lanczos Algorithm + +#+BEGIN_SRC c + for (int i = 0; i < n; i++) { + sum = a0; + v_dot_v(a[i], x); + + b[i] = sum; + } +#+END_SRC diff --git a/Homework/math4610/notes/Oct-11.org b/Homework/math4610/notes/Oct-11.org new file mode 100644 index 0000000..575ea74 --- /dev/null +++ b/Homework/math4610/notes/Oct-11.org @@ -0,0 +1,15 @@ +* Diagonal Dominance +Suppose that A \in R^{n \times n} is diagonally dominant then Gaussian eliminiation of A produces no zero pivot +elements. + +Def. A \in R^{n \times n} is diagonally dominant if for each i=1,2,...n |a_{i,i}| \geq \Sigma_{j=1}^n |a_i,j| + + +* To test solution code: + [[1] + [1] +Set y = [\cdots] \in R^n + [1]] + +Compute b=Ay +Solve Ax=b diff --git a/Homework/math4610/notes/Oct-13.org b/Homework/math4610/notes/Oct-13.org new file mode 100644 index 0000000..853a6d6 --- /dev/null +++ b/Homework/math4610/notes/Oct-13.org @@ -0,0 +1,8 @@ +* Root Finding +Finx x \in R such that f(x) = 0 + +If g(x) is a function and we want to find x such that g(x) is +extremal then we find x \in R such that g'(x) = 0 + + +Hanging Cable Problem y(x) = c_1 cosh(\frac{x- c_2}{c_2}) diff --git a/Homework/math4610/notes/Oct-16.org b/Homework/math4610/notes/Oct-16.org new file mode 100644 index 0000000..1406737 --- /dev/null +++ b/Homework/math4610/notes/Oct-16.org @@ -0,0 +1,77 @@ +Find x \in R st f(x) = 0 + +if f(x^*) = 0 then define x^* = g(x^*) = x^* + f(x^*) + +Suppose we approximate x^* by x_0. Then Using the fixed point equations: + +x_1 = g(x_0) = x_0 + f(x_0) +x_2 = g(g_1) \cdots x_{k+1} = g(x_k) + +This generates a sequence of approximations to x^* + +{X_k} \rightarrow x^* + +The algorithm is: Given f(x), x_0, compute x_{k+1} = g(x_k), k = 0, 1, 2, \cdots += x_k + f(x_k) + +Examples for g(x) + +1. x_{k+1} = x_k + f(x_k) +2. x_{k+1} = x_k - f(x_k) +3. x_{k+1} = x_k - mf(x_k) +4. x_{k+q} = s_k - sin(f(x_k)) + +x^* = root of f +y^* = solution of y^* = g(y^*) + +| x^* - y^* | = x^* - (y^* - f(y^*)) +|x_{k+1} - x^* | = | g(x_k) - g(x^*) | + = |g(x^*) + g'(x^k)(x_k - x^*) + \cdots) - g(x^*)| + = |g'(x^*)(x_k - x^*) + hot| + \leq | g'(x^*)(x_k - x^*)| + (pos val) + \leq |g'(x^*)| (|x_k - x^*|) + +\Rightarrow |x_{k+1} - x^*| \leq |g'(x^*)| \cdot |x_k - x^*| + +For this to converge, we need |g'(x^*)| \lt 1 + +* Example +f(x) = xe^{-x} + +Then x^* = 0 + +If we construct g(x) = 10x + xe^-x + +Then g'(x) = 10 + (e^-x - xe^-x) \Rightarrow g'(x) = 10 + e^0 - 0 = 11 (this wouldn't converge) + +However if g(x)) = x - (xe^-x), g'(x) = 1 - (e^-x - xe^-x) \Rightarrow g'(x^*) = 0 + +Then assume x_0 = 1/10 +Then x_1 = g(x_0) = 1/10 - 1/10(e^{-1/10}) +\cdots + +* More General, Robust Algorithm +** Theorem: Intermediate Value Theorem +Suppose that f(x) is a continuous function on [a, b] then + +\lim_{x -> x_0} (f(x)) = f(x_0) + +For all x_0 \in (a, b) and at the endpoints: + +\lim_{a^+} f(x) = f(a) +\lim_{x -> b^-} f(x) = f(b) + +Then if s is a number between f(a) and f(b), there exists a point c \in (a, b) such that f(c) = s. + +To use this to ensure there is a root, we just take evaluations f(a) and f(b) that cross 0 + +So the condition we construct is: +f(a) \cdot f(b) \lt 0 + +** Next Step: compute the midpoint of [a, b] +c = 1/2 (a + b) + +do binary search on c by taking this midpoint and ensuring f(a) \cdot f(c) \lt 0 or f(c) \cdot f(b) \lt 0 is met, +choosing the correct interval + + diff --git a/Homework/math4610/notes/Oct-18.org b/Homework/math4610/notes/Oct-18.org new file mode 100644 index 0000000..0104164 --- /dev/null +++ b/Homework/math4610/notes/Oct-18.org @@ -0,0 +1,18 @@ +Error Analysis Of Bisection Root Finding: + +e_0 \le b - a = b_0 - a_0 +e_1 \le b_1 - a_1 = 1/2(b_0 - a_0) +e_2 \le b_2 - a_2 = 1/2(b_1 - a_1) = (1/2)^2(b_0 - a_0) +e_k \le b_k - a_k = 1/2(b_{k-1} - a_{k-1}) = \cdots = (1/2)^k (b_0 - a_0) + + +e_k \le (1/2)^k (b_0 - a_0) = tolerance +\Rightarrow log(1/2^k) + log(b_0 - a_0) = log(tolerance) +\Rightarrow k log(1/2) + log(tolerance) - log(b_0 - a_0) +\Rightarrow k log(1/2) = log(tolerance / (b_0 - a_0)) +\Rightarrow k \ge log(tolerance / (b_0 - a_0)) / log(1/2) + +The Bisection Method applied to an interval [a, b] for a continous function will reduce the error +each time through by at least one half. + +| x_{k+1} - x_k | \le 1/2|x_k - x^* | diff --git a/Homework/math4610/notes/Oct-27.org b/Homework/math4610/notes/Oct-27.org new file mode 100644 index 0000000..6d23576 --- /dev/null +++ b/Homework/math4610/notes/Oct-27.org @@ -0,0 +1,26 @@ +Use a bisection criterion for a start + +Hybrid Method: combine Bisection and Higher Order Method: +- Newton's Method +- Secant Method (Newton's method with secant approx.) + + +#+BEGIN_SRC c +fa = f(a) +fb = f(b) +if (fa * fb >= 0) return + +error = 10 * tol +iter = 0 + +while (error > tol && iter < maxiter) { +x0 = 0.5 * (a + b) +x1 = x0 - f(x0) / f'(x0) +if (abs(x1 - x0) > 0.5 * (b - a)) { +// do bisection +} else{ +// do newton's method +} +} +#+END_SRC + diff --git a/Homework/math4610/notes/Oct-30.org b/Homework/math4610/notes/Oct-30.org new file mode 100644 index 0000000..7d6ee03 --- /dev/null +++ b/Homework/math4610/notes/Oct-30.org @@ -0,0 +1,34 @@ +* Power Method for computing the largest eigenvalue of a square matrix + +An eigenvector, v \in R^n is a nonzero vector such that for some number, \lambda \in C, Av = \lambda v +\Rightarrow || v || = 1 + + +Suppose we start with some vector v and assume, v = \alpha_0 v_0 + \alpha_1 v_1 + \cdots + \alpha_n v_n, where {v_1, \cdots, v_n} +are the eigenvectors of A. Assume {v_1, \cdots, v_n} is a basis for R^n + +We can order the eigenvalues such that \lambda_1 \ge \lambda_2 \ge \lambda_3 \ge \cdots \ge \lambda_n + +Compute u = Av += A(\alpha_1 v_1 + \cdots + \alpha_n v_n) += \alpha_1 Av_1 + A(\cdots) + \alpha_n A v_n += \alpha_1 \lambda_1 v_1 + \alpha_2 \lambda_2 v_2 + \cdots + \alpha_n \lambda_n v_n + +w = A (Av) += \alpha_1 \lambda_1^2 v_1 + \alpha_2 \lambda_2^2 v_2 + \cdots + \alpha_n \lambda_n^2 v_n + +Thus, +A^k v = \alpha_1 \lambda_1^k v_1 + \alpha_2 \lambda_2^k v_2 + \cdots + \alpha_n \lambda_n^k v_n += \lambda_1^k ( \alpha_1 v_1 + \alpha_2 \frac{\lambda_2^k}{\lambda_1^k} v_2 + \cdots + \alpha_n \frac{\lambda_3^k}{\lambda_1^k} v_n) + +As k \rightarrow \infty +A^k v = \lambda_1^k (\alpha_1 v_1) + \text{negligble terms} + +Algorithm: +v \ne 0 with v \in R^n +y = Av = \alpha_1 v_1 + \cdots + \alpha_n v_n + +w = \frac{1}{||y||} \cdot y + +Rayleigh Quotient: +If $v$ is an eigenvector of A with eigenvalue \lambda then \frac{v^T A v}{v^T v} = \lambda diff --git a/Homework/math4610/notes/Oct-4.org b/Homework/math4610/notes/Oct-4.org new file mode 100644 index 0000000..8b8466f --- /dev/null +++ b/Homework/math4610/notes/Oct-4.org @@ -0,0 +1,22 @@ +[[ a_{11} a_{12} \cdots a_{1n} | b_1] + [ 0 (a_{22} - \frac{a_{}_{21}}{a_{22}}a_{11}) \cdots a_{2n} | b_2 - \frac{a_{21}}{a_{11}}b_1 ]] + +#+BEGIN_SRC c + for (int i = 1; i < n; i++) { + float factor = -a[i][0] / a[0][0]; + for (int j = 1; j < n; j++) { + a[i][j] = a[i][j] + factor * a[0][j]; + } + b[i] = b[i] + factor * b[0]; + } + + for (int k = 0; k < (n - 1); k++) { + for (int i = k+1; i < n; i++) { + float factor = -a[i][k] / a[k][k]; + for (int j = k+1; j < n; j++) { + a[i][j] = a[i][j] + factor * a[j][k]; + } + b[i] = b[i] + factor*b[k]; + } + } +#+END_SRC diff --git a/Homework/math4610/notes/Oct-6.org b/Homework/math4610/notes/Oct-6.org new file mode 100644 index 0000000..8cbff29 --- /dev/null +++ b/Homework/math4610/notes/Oct-6.org @@ -0,0 +1,13 @@ +#+BEGIN_SRC c + for (int k = 0; i < (n - 1); k++) { + for (int i = k+1; i< n; i++) { + float factor = a[i][k] / a[k][k]; + for (int j = k+1; j < k; j++) { + a[i][j] = a[i][j] - factor * a[k][j]; + } + b[i] = b[i] - factor * b[k]; + } + } +#+END_SRC + + diff --git a/Homework/math4610/notes/Sep-11.org b/Homework/math4610/notes/Sep-11.org new file mode 100644 index 0000000..3d71f2f --- /dev/null +++ b/Homework/math4610/notes/Sep-11.org @@ -0,0 +1,94 @@ +#+TITLE: Errors +#+AUTHOR: Elizabeth Hunt +#+STARTUP: entitiespretty fold inlineimages +#+LATEX_HEADER: \notindent \notag \usepackage{amsmath} \usepackage[a4paper,margin=1in,landscape]{geometry} +#+LATEX: \setlength\parindent{0pt} +#+OPTIONS: toc:nil + +* Errors +$x,y \in \mathds{R}$, using y as a way to approximate x. Then the +absolute error of in approximating x w/ y is $e_{abs}(x, y) = |x-y|$. + +and the relative error is $e_{rel}(x, y) = \frac{|x-y|}{|x|}$ + +Table of Errors + +#+BEGIN_SRC lisp :results table + (load "../cl/lizfcm.asd") + (ql:quickload 'lizfcm) + + (defun eabs (x y) (abs (- x y))) + (defun erel (x y) (/ (abs (- x y)) (abs x))) + + (defparameter *u-v* '( + (1 0.99) + (1 1.01) + (-1.5 -1.2) + (100 99.9) + (100 99) + )) + + (lizfcm.utils:table (:headers '("u" "v" "e_{abs}" "e_{rel}") + :domain-order (u v) + :domain-values *u-v*) + (eabs u v) + (erel u v)) +#+END_SRC + +#+RESULTS: +| u | v | e_{abs} | e_{rel} | +| 1 | 0.99 | 0.00999999 | 0.00999999 | +| 1 | 1.01 | 0.00999999 | 0.00999999 | +| -1.5 | -1.2 | 0.29999995 | 0.19999997 | +| 100 | 99.9 | 0.099998474 | 0.0009999848 | +| 100 | 99 | 1 | 1/100 | + + +Look at $u \approx 0$ then $v \approx 0$, $e_{abs}$ is better error since $e_{rel}$ is high. + +* Vector spaces & measures +Suppose we want solutions fo a linear system of the form $Ax = b$, and we want to approximate $x$, +we need to find a form of "distance" between vectors in $\mathds{R}^n$ + +** Vector Distances +A norm on a vector space $|| v ||$ is a function from $\mathds{R}^n$ such that: + +1. $||v|| \geq 0$ for all $v \in \mathds{R}^n$ and $||v|| = \Leftrightarrow v = 0$ +2. $||cv|| = |c| ||v||$ for all $c \in \mathds{R}, v \in \mathds{R}^n$ +3. $||x + y|| \leq ||x|| + ||y|| \forall x,y \in \mathds{R}^n$ + +*** Example norms: +$||v||_2 = || [v_1, v_2, \dots v_n] || = (v_1^2 + v_2^2 + \dots + v_n^2)^{}^{\frac{1}{2}}$ + +$||v||_1 = |v_1| + |v_2| + \dots + |v_n|$ + +$||v||_{\infty} = \text{max}(|v_i|)$ (most restriction) + +p-norm: +$||v||_p = \sum_{i=1}^{h} (|v_i|^p)^{\frac{1}{p}}$ + +** Length +The length of a vector in a given norm is $||v|| \forall v \in \mathds{R}^n$ + +All norms on finite dimensional vectors are equivalent. Then exist constants +$\alpha, \beta > 0 \ni \alpha ||v||_p \leq ||v||_q \leq \beta||v||_p$ + +** Distance +Let $u,v$ be vectors in $\mathds{R}^n$ then the distance is $||u - v||$ by some norm: +$e_{abs} = d(v, u) = ||u - v||$ + +The relative errors is: + +$e_{rel} = \frac{||u - v||}{||v||}$ + + +** Approxmiating Solutions to $Ax = b$ +We define the residual vector $r(x) = b - Ax$ + +If $x$ is the exact solution, then $r(x) = 0$. + +Then we can measure the "correctness" of the approximated solution on the norm of the +residual. We want to minimize the norm. + +But, $r(y) = b - Ay \approx 0 \nRightarrow y \equiv x$, if $A$ is not invertible. + diff --git a/Homework/math4610/notes/Sep-13.org b/Homework/math4610/notes/Sep-13.org new file mode 100644 index 0000000..0ebff2b --- /dev/null +++ b/Homework/math4610/notes/Sep-13.org @@ -0,0 +1,16 @@ +* Homework 2 +1. maceps - single precision + +2. maceps - double precision + +3. 2-norm of a vector + +4. 1-norm of a vector + +5. infinity-norm of a vector (max-norm) + +6. 2-norm distance between 2 vectors + +7. 1-norm distance between 2 vectors + +8. infinity-norm distance diff --git a/Homework/math4610/notes/Sep-15.org b/Homework/math4610/notes/Sep-15.org new file mode 100644 index 0000000..8a64089 --- /dev/null +++ b/Homework/math4610/notes/Sep-15.org @@ -0,0 +1,52 @@ +* Taylor Series Approx. +Suppose f has $\infty$ many derivatives near a point a. Then the taylor series is given by + +$f(x) = \Sigma_{n=0}^{\infty} \frac{f^{(n)}(a)}{n!}(x-a)^n$ + +For increment notation we can write + +$f(a + h) = f(a) + f'(a)(a+h - a) + \dots$ + +$= \Sigma_{n=0}^{\infty} \frac{f^{(n)}(a)}{h!} (h^n)$ + +Consider the approximation + +$e = |f'(a) - \frac{f(a + h) - f(a)}{h}| = |f'(a) - \frac{1}{h}(f(a + h) - f(a))|$ + +Substituting... + +$= |f'(a) - \frac{1}{h}((f(a) + f'(a) h + \frac{f''(a)}{2} h^2 + \cdots) - f(a))|$ + +$f(a) - f(a) = 0$... and $distribute the h$ + +$= |-1/2 f''(a) h + \frac{1}{6}f'''(a)h^2 \cdots|$ + +** With Remainder +We can determine for some u $f(a + h) = f(a) + f'(a)h + \frac{1}{2}f''(u)h^2$ + +and so the error is $e = |f'(a) - \frac{f(a + h) - f(a)}{h}| = |\frac{h}{2}f''(u)|$ + +- [https://openstax.org/books/calculus-volume-2/pages/6-3-taylor-and-maclaurin-series] + + > Taylor's Theorem w/ Remainder + + +** Of Deriviatives + +Again, $f'(a) \approx \frac{f(a+h) - f(a)}{h}$, + +$e = |\frac{1}{2} f''(a) + \frac{1}{3!}h^2 f'''(a) + \cdots$ + +$R_2 = \frac{h}{2} f''(\xi)$ + +$|\frac{h}{2} f''(\xi)| \leq M h^1$ + +$M = \frac{1}{2}|f'(\xi)|$ + +*** Another approximation + +$\text{err} = |f'(a) - \frac{f(a) - f(a - h)}{h}|$ + +$= f'(a) - \frac{1}{h}(f(a) - (f(a) + f'(a)(a - (a - h)) + \frac{1}{2}f''(a)(a-(a-h))^2 + \cdots))$ + +$= |f'(a) - (f'(a) + \frac{1}{2}f''(a)h)|$ + diff --git a/Homework/math4610/notes/Sep-15.pdf b/Homework/math4610/notes/Sep-15.pdf Binary files differnew file mode 100644 index 0000000..43a4f34 --- /dev/null +++ b/Homework/math4610/notes/Sep-15.pdf diff --git a/Homework/math4610/notes/Sep-15.tex b/Homework/math4610/notes/Sep-15.tex new file mode 100644 index 0000000..52610ed --- /dev/null +++ b/Homework/math4610/notes/Sep-15.tex @@ -0,0 +1,88 @@ +% Created 2023-09-29 Fri 10:00 +% Intended LaTeX compiler: pdflatex +\documentclass[11pt]{article} +\usepackage[utf8]{inputenc} +\usepackage[T1]{fontenc} +\usepackage{graphicx} +\usepackage{longtable} +\usepackage{wrapfig} +\usepackage{rotating} +\usepackage[normalem]{ulem} +\usepackage{amsmath} +\usepackage{amssymb} +\usepackage{capt-of} +\usepackage{hyperref} +\author{Elizabeth Hunt} +\date{\today} +\title{} +\hypersetup{ + pdfauthor={Elizabeth Hunt}, + pdftitle={}, + pdfkeywords={}, + pdfsubject={}, + pdfcreator={Emacs 28.2 (Org mode 9.7-pre)}, + pdflang={English}} +\begin{document} + +\tableofcontents + +\section{Taylor Series Approx.} +\label{sec:orgcc72ed1} +Suppose f has \(\infty\) many derivatives near a point a. Then the taylor series is given by + +\(f(x) = \Sigma_{n=0}^{\infty} \frac{f^{(n)}(a)}{n!}(x-a)^n\) + +For increment notation we can write + +\(f(a + h) = f(a) + f'(a)(a+h - a) + \dots\) + +\(= \Sigma_{n=0}^{\infty} \frac{f^{(n)}(a)}{h!} (h^n)\) + +Consider the approximation + +\(e = |f'(a) - \frac{f(a + h) - f(a)}{h}| = |f'(a) - \frac{1}{h}(f(a + h) - f(a))|\) + +Substituting\ldots{} + +\(= |f'(a) - \frac{1}{h}((f(a) + f'(a) h + \frac{f''(a)}{2} h^2 + \cdots) - f(a))|\) + +\(f(a) - f(a) = 0\)\ldots{} and \(distribute the h\) + +\(= |-1/2 f''(a) h + \frac{1}{6}f'''(a)h^2 \cdots|\) + +\subsection{With Remainder} +\label{sec:org7dfd6c7} +We can determine for some u \(f(a + h) = f(a) + f'(a)h + \frac{1}{2}f''(u)h^2\) + +and so the error is \(e = |f'(a) - \frac{f(a + h) - f(a)}{h}| = |\frac{h}{2}f''(u)|\) + +\begin{itemize} +\item\relax [\url{https://openstax.org/books/calculus-volume-2/pages/6-3-taylor-and-maclaurin-series}] +\begin{itemize} +\item > Taylor's Theorem w/ Remainder +\end{itemize} +\end{itemize} + + +\subsection{Of Deriviatives} +\label{sec:org1ec7c9b} + +Again, \(f'(a) \approx \frac{f(a+h) - f(a)}{h}\), + +\(e = |\frac{1}{2} f''(a) + \frac{1}{3!}h^2 f'''(a) + \cdots\) + +\(R_2 = \frac{h}{2} f''(u)\) + +\(|\frac{h}{2} f''(u)| \leq M h^1\) + +\(M = \frac{1}{2}|f'(u)|\) + +\subsubsection{Another approximation} +\label{sec:org16193b9} + +\(\text{err} = |f'(a) - \frac{f(a) - f(a - h)}{h}|\) + +\(= f'(a) - \frac{1}{h}(f(a) - (f(a) + f'(a)(a - (a - h)) + \frac{1}{2}f''(a)(a-(a-h))^2 + \cdots))\) + +\(= |f'(a) - \frac{1}{h}(f'(a) + \frac{1}{2}f''(a)h)|\) +\end{document}
\ No newline at end of file diff --git a/Homework/math4610/notes/Sep-20.org b/Homework/math4610/notes/Sep-20.org new file mode 100644 index 0000000..ba067bb --- /dev/null +++ b/Homework/math4610/notes/Sep-20.org @@ -0,0 +1,21 @@ +* Review & Summary +Approx f'(a) with + ++ forward difference $f'(a) \approx \frac{f(a+h) - f(a)}{h}$ + ++ backward difference $f'(a) \approx \frac{f(a) - f(a-h)}{h}$ + ++ central difference $f'(a) \approx \frac{f(a+h) - f(a-h)}{2h}$ + +** Taylor Series +given $C = \frac{1}{2}(|f''(\xi)|) \cdot h^1$ + +with f.d. $e_{\text{abs}} \leq Ch^1$ + +b.d. $e_{\text{abs}} \leq Ch^1$ + +c.d. $e_{\text{abs}} \leq Ch^2$ + +$e_{\text{abs}} \leq Ch^r$ + +$log(e(h)) \leq log(ch^r) = log(C) + log(h^r) = log(C) + rlog(h)$ diff --git a/Homework/math4610/notes/Sep-22.org b/Homework/math4610/notes/Sep-22.org new file mode 100644 index 0000000..b631e3b --- /dev/null +++ b/Homework/math4610/notes/Sep-22.org @@ -0,0 +1,45 @@ +* regression +consider the generic problem of fitting a dataset to a linear polynomial + +given discrete f: x \rightarrow y + +interpolation: y = a + bx + +[[1 x_0] [[y_0] + [1 x_1] \cdot [[a] = [y_1] + [1 x_n]] [b]] [y_n]] + +consider p \in col(A) + +then y = p + q for some q \cdot p = 0 + +then we can generate n \in col(A) by $Az$ and n must be orthogonal to q as well + +(Az)^T \cdot q = 0 = (Az)^T (y - p) + +0 = (z^T A^T)(y - Ax) + = z^T (A^T y - A^T A x) + = A^T Ax + = A^T y + + +A^T A = [[n+1 \Sigma_{n=0}^n x_n] + [\Sigma_{n=0}^n x_n \Sigma_{n=0}^n x_n^2]] + +A^T y = [[\Sigma_{n=0}^n y_n] + [\Sigma_{n=0}^n x_n y_n]] + +a_11 = n+1 +a_12 = \Sigma_{n=0}^n x_n +a_21 = a_12 +a_22 = \Sigma_{n=0}^n x_n^2 +b_1 = \Sigma_{n=0}^n y_n +b_2 = \Sigma_{n=0}^n x_n y_n + +then apply this with: + +log(e(h)) \leq log(C) + rlog(h) + +* homework 3: + +two columns \Rightarrow coefficients for linear regression diff --git a/Homework/math4610/notes/Sep-25.org b/Homework/math4610/notes/Sep-25.org new file mode 100644 index 0000000..b2d63e3 --- /dev/null +++ b/Homework/math4610/notes/Sep-25.org @@ -0,0 +1,48 @@ +ex: erfc(x) = \int_{0}^x (\frac{2}{\sqrt{pi}})e^{-t^2 }dt +ex: IVP \frac{dP}{dt} = \alpha P - \beta P^2 + P(0) = P_0 + +Explicit Euler Method + +$\frac{P(t + \Delta t) - P(t)}{\Delta t} \approx \alpha P(t) - \beta P^2(t)$ + +From 0 \rightarrow T +P(T) \approx n steps + +* Steps +** Calculus: defference quotient +$f'(a) \approx \frac{f(a+h) - f(a)}{h}$ + +** Test. +Roundoff for h \approx 0 + +** Calculus: Taylor Serioes w/ Remainder +$e_{abs}(h) \leq Ch^r$ + +(see Sep-20 . Taylor Series) + +* Pseudo Code +#+BEGIN_SRC python + for i in range(n): + a12 = a12 + x[i+1] + a22 = a22 + x[i+1]**2 + a21 = a12 + b1 = y[0] + b2 = y[0] * x[0] + for i in range(n): + b1 = b1 + y[i+1] + b2 = b2 + y[i+1]*x[i+1] + detA = a22*a11 - a12*a21 + c = (a22*b1 - a12*b2) / detA + d = (-a21 * b1 + a11 * b2) / detA + + return (c, d) +#+END_SRC + +* Error +We want +$e_k = |df(h_kk) - f'(a)|$ + +$= |df(h_k) - df(h_m) + df(h_m) - f'(a)|$ + +$\leq |df(h_k) - df(h_m)| + |df(h_m) - f'(a)|$ and $|df(h_m) - f'(a)|$ is negligible diff --git a/Homework/math4610/src/approx_derivative.c b/Homework/math4610/src/approx_derivative.c new file mode 100644 index 0000000..63d0b05 --- /dev/null +++ b/Homework/math4610/src/approx_derivative.c @@ -0,0 +1,38 @@ +#include "lizfcm.h" +#include <assert.h> + +double central_derivative_at(double (*f)(double), double a, double h) { + assert(h > 0); + + double x2 = a + h; + double x1 = a - h; + + double y2 = f(x2); + double y1 = f(x1); + + return (y2 - y1) / (x2 - x1); +} + +double forward_derivative_at(double (*f)(double), double a, double h) { + assert(h > 0); + + double x2 = a + h; + double x1 = a; + + double y2 = f(x2); + double y1 = f(x1); + + return (y2 - y1) / (x2 - x1); +} + +double backward_derivative_at(double (*f)(double), double a, double h) { + assert(h > 0); + + double x2 = a; + double x1 = a - h; + + double y2 = f(x2); + double y1 = f(x1); + + return (y2 - y1) / (x2 - x1); +} diff --git a/Homework/math4610/src/eigen.c b/Homework/math4610/src/eigen.c new file mode 100644 index 0000000..49cc0e4 --- /dev/null +++ b/Homework/math4610/src/eigen.c @@ -0,0 +1,116 @@ +#include "lizfcm.h" +#include <assert.h> +#include <math.h> +#include <stdio.h> +#include <string.h> + +double least_dominant_eigenvalue(Matrix_double *m, Array_double *v, + double tolerance, size_t max_iterations) { + return shift_inverse_power_eigenvalue(m, v, 0.0, tolerance, max_iterations); +} + +double dominant_eigenvalue(Matrix_double *m, Array_double *v, double tolerance, + size_t max_iterations) { + assert(m->rows == m->cols); + assert(m->rows == v->size); + + double error = tolerance; + size_t iter = max_iterations; + double lambda = 0.0; + Array_double *eigenvector_1 = copy_vector(v); + + while (error >= tolerance && (--iter) > 0) { + Array_double *eigenvector_2 = m_dot_v(m, eigenvector_1); + Array_double *normalized_eigenvector_2 = + scale_v(eigenvector_2, 1.0 / linf_norm(eigenvector_2)); + free_vector(eigenvector_2); + eigenvector_2 = normalized_eigenvector_2; + + Array_double *mx = m_dot_v(m, eigenvector_2); + double new_lambda = + v_dot_v(mx, eigenvector_2) / v_dot_v(eigenvector_2, eigenvector_2); + + error = fabs(new_lambda - lambda); + lambda = new_lambda; + free_vector(eigenvector_1); + eigenvector_1 = eigenvector_2; + } + + return lambda; +} + +double shift_inverse_power_eigenvalue(Matrix_double *m, Array_double *v, + double shift, double tolerance, + size_t max_iterations) { + assert(m->rows == m->cols); + assert(m->rows == v->size); + + Matrix_double *m_c = copy_matrix(m); + for (size_t y = 0; y < m_c->rows; ++y) + m_c->data[y]->data[y] = m_c->data[y]->data[y] - shift; + + double error = tolerance; + size_t iter = max_iterations; + double lambda = shift; + Array_double *eigenvector_1 = copy_vector(v); + + while (error >= tolerance && (--iter) > 0) { + Array_double *eigenvector_2 = solve_matrix_lu_bsubst(m_c, eigenvector_1); + Array_double *normalized_eigenvector_2 = + scale_v(eigenvector_2, 1.0 / linf_norm(eigenvector_2)); + free_vector(eigenvector_2); + + Array_double *mx = m_dot_v(m, normalized_eigenvector_2); + double new_lambda = + v_dot_v(mx, normalized_eigenvector_2) / + v_dot_v(normalized_eigenvector_2, normalized_eigenvector_2); + + error = fabs(new_lambda - lambda); + lambda = new_lambda; + free_vector(eigenvector_1); + eigenvector_1 = normalized_eigenvector_2; + } + + return lambda; +} + +Array_double *partition_find_eigenvalues(Matrix_double *m, + Matrix_double *guesses, + double tolerance, + size_t max_iterations) { + assert(guesses->rows >= + 2); // we need at least, the most and least dominant eigenvalues + + double end = dominant_eigenvalue(m, guesses->data[guesses->rows - 1], + tolerance, max_iterations); + double begin = + least_dominant_eigenvalue(m, guesses->data[0], tolerance, max_iterations); + + double delta = (end - begin) / guesses->rows; + Array_double *eigenvalues = InitArrayWithSize(double, guesses->rows, 0.0); + for (size_t i = 0; i < guesses->rows; i++) { + double box_midpoint = ((delta * i) + (delta * (i + 1))) / 2; + + double nearest_eigenvalue = shift_inverse_power_eigenvalue( + m, guesses->data[i], box_midpoint, tolerance, max_iterations); + + eigenvalues->data[i] = nearest_eigenvalue; + } + + return eigenvalues; +} + +Matrix_double *leslie_matrix(Array_double *age_class_surivor_ratio, + Array_double *age_class_offspring) { + assert(age_class_surivor_ratio->size + 1 == age_class_offspring->size); + + Matrix_double *leslie = InitMatrixWithSize(double, age_class_offspring->size, + age_class_offspring->size, 0.0); + + free_vector(leslie->data[0]); + leslie->data[0] = copy_vector(age_class_offspring); + + for (size_t i = 0; i < age_class_surivor_ratio->size; i++) + leslie->data[i + 1]->data[i] = age_class_surivor_ratio->data[i]; + return leslie; +} diff --git a/Homework/math4610/src/lin.c b/Homework/math4610/src/lin.c new file mode 100644 index 0000000..d531025 --- /dev/null +++ b/Homework/math4610/src/lin.c @@ -0,0 +1,19 @@ +#include "lizfcm.h" +#include <assert.h> + +Line *least_squares_lin_reg(Array_double *x, Array_double *y) { + assert(x->size == y->size); + + uint64_t n = x->size; + double sum_x = sum_v(x); + double sum_y = sum_v(y); + double sum_xy = v_dot_v(x, y); + double sum_xx = v_dot_v(x, x); + double denom = ((n * sum_xx) - (sum_x * sum_x)); + + Line *line = malloc(sizeof(Line)); + line->m = ((sum_xy * n) - (sum_x * sum_y)) / denom; + line->a = ((sum_y * sum_xx) - (sum_x * sum_xy)) / denom; + + return line; +} diff --git a/Homework/math4610/src/maceps.c b/Homework/math4610/src/maceps.c new file mode 100644 index 0000000..23bc9db --- /dev/null +++ b/Homework/math4610/src/maceps.c @@ -0,0 +1,28 @@ +#include "lizfcm.h" +#include <math.h> + +float smaceps() { + float one = 1.0; + float machine_epsilon = 1.0; + float one_approx = one + machine_epsilon; + + while (fabsf(one_approx - one) > 0) { + machine_epsilon /= 2; + one_approx = one + machine_epsilon; + } + + return machine_epsilon; +} + +double dmaceps() { + double one = 1.0; + double machine_epsilon = 1.0; + double one_approx = one + machine_epsilon; + + while (fabs(one_approx - one) > 0) { + machine_epsilon /= 2; + one_approx = one + machine_epsilon; + } + + return machine_epsilon; +} diff --git a/Homework/math4610/src/matrix.c b/Homework/math4610/src/matrix.c new file mode 100644 index 0000000..901a426 --- /dev/null +++ b/Homework/math4610/src/matrix.c @@ -0,0 +1,346 @@ +#include "lizfcm.h" +#include <assert.h> +#include <math.h> +#include <stdio.h> +#include <string.h> + +Array_double *m_dot_v(Matrix_double *m, Array_double *v) { + assert(v->size == m->cols); + + Array_double *product = copy_vector(v); + + for (size_t row = 0; row < v->size; ++row) + product->data[row] = v_dot_v(m->data[row], v); + + return product; +} + +Array_double *col_v(Matrix_double *m, size_t x) { + assert(x < m->cols); + + Array_double *col = InitArrayWithSize(double, m->rows, 0.0); + for (size_t y = 0; y < m->rows; y++) + col->data[y] = m->data[y]->data[x]; + + return col; +} + +Matrix_double *m_dot_m(Matrix_double *a, Matrix_double *b) { + assert(a->cols == b->rows); + + Matrix_double *prod = InitMatrixWithSize(double, a->rows, b->cols, 0.0); + + Array_double *curr_col; + for (size_t y = 0; y < a->rows; y++) { + for (size_t x = 0; x < b->cols; x++) { + curr_col = col_v(b, x); + prod->data[y]->data[x] = v_dot_v(curr_col, a->data[y]); + free_vector(curr_col); + } + } + + return prod; +} + +Matrix_double *transpose(Matrix_double *m) { + Matrix_double *transposed = InitMatrixWithSize(double, m->cols, m->rows, 0.0); + + for (size_t x = 0; x < m->rows; x++) + for (size_t y = 0; y < m->cols; y++) + transposed->data[y]->data[x] = m->data[x]->data[y]; + + return transposed; +} + +Matrix_double *put_identity_diagonal(Matrix_double *m) { + assert(m->rows == m->cols); + Matrix_double *copy = copy_matrix(m); + for (size_t y = 0; y < m->rows; ++y) + copy->data[y]->data[y] = 1.0; + return copy; +} + +Matrix_double *copy_matrix(Matrix_double *m) { + Matrix_double *copy = InitMatrixWithSize(double, m->rows, m->cols, 0.0); + for (size_t y = 0; y < copy->rows; y++) { + free_vector(copy->data[y]); + copy->data[y] = copy_vector(m->data[y]); + } + return copy; +} + +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_matrix(l_empt); + + 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"); + 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 (denom == 0) { + printf("ERROR: non-factorable matrix\n"); + assert(false); + } + + 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]); + + u->data[y] = added; + l->data[y]->data[x] = -factor; + } + } + } + + u_l[0] = u; + u_l[1] = l; + return u_l; +} + +Array_double *bsubst(Matrix_double *u, Array_double *b) { + assert(u->rows == b->size && u->cols == u->rows); + + Array_double *x = copy_vector(b); + for (int64_t row = b->size - 1; row >= 0; row--) { + for (size_t col = b->size - 1; col > row; col--) + x->data[row] -= x->data[col] * u->data[row]->data[col]; + x->data[row] /= u->data[row]->data[row]; + } + return x; +} + +Array_double *fsubst(Matrix_double *l, Array_double *b) { + assert(l->rows == b->size && l->cols == l->rows); + + Array_double *x = copy_vector(b); + + for (size_t row = 0; row < b->size; row++) { + for (size_t col = 0; col < row; col++) + x->data[row] -= x->data[col] * l->data[row]->data[col]; + x->data[row] /= l->data[row]->data[row]; + } + + return x; +} + +Array_double *solve_matrix_lu_bsubst(Matrix_double *m, Array_double *b) { + assert(b->size == m->rows); + assert(m->rows == m->cols); + + Array_double *x = copy_vector(b); + Matrix_double **u_l = lu_decomp(m); + Matrix_double *u = u_l[0]; + Matrix_double *l = u_l[1]; + + Array_double *b_fsub = fsubst(l, b); + x = bsubst(u, b_fsub); + free_vector(b_fsub); + + free_matrix(u); + free_matrix(l); + free(u_l); + + return x; +} + +Matrix_double *gaussian_elimination(Matrix_double *m) { + uint64_t h = 0, k = 0; + + Matrix_double *m_cp = copy_matrix(m); + + while (h < m_cp->rows && k < m_cp->cols) { + uint64_t max_row = h; + double max_val = 0.0; + + for (uint64_t row = h; row < m_cp->rows; row++) { + double val = fabs(m_cp->data[row]->data[k]); + if (val > max_val) { + max_val = val; + max_row = row; + } + } + + if (max_val == 0.0) { + k++; + continue; + } + + if (max_row != h) { + 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; +} + +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; +} + +Array_double *jacobi_solve(Matrix_double *m, Array_double *b, + double l2_convergence_tolerance, + size_t max_iterations) { + assert(m->rows == m->cols); + assert(b->size == m->cols); + size_t iter = max_iterations; + + Array_double *x_k = InitArrayWithSize(double, b->size, 0.0); + Array_double *x_k_1 = + InitArrayWithSize(double, b->size, rand_from(0.1, 10.0)); + + while ((--iter) > 0 && l2_distance(x_k_1, x_k) > l2_convergence_tolerance) { + for (size_t i = 0; i < m->rows; i++) { + double delta = 0.0; + for (size_t j = 0; j < m->cols; j++) { + if (i == j) + continue; + delta += m->data[i]->data[j] * x_k->data[j]; + } + x_k_1->data[i] = (b->data[i] - delta) / m->data[i]->data[i]; + } + + Array_double *tmp = x_k; + x_k = x_k_1; + x_k_1 = tmp; + } + + free_vector(x_k); + return x_k_1; +} + +Array_double *gauss_siedel_solve(Matrix_double *m, Array_double *b, + double l2_convergence_tolerance, + size_t max_iterations) { + assert(m->rows == m->cols); + assert(b->size == m->cols); + size_t iter = max_iterations; + + Array_double *x_k = InitArrayWithSize(double, b->size, 0.0); + Array_double *x_k_1 = + InitArrayWithSize(double, b->size, rand_from(0.1, 10.0)); + + while ((--iter) > 0) { + for (size_t i = 0; i < x_k->size; i++) + x_k->data[i] = x_k_1->data[i]; + + for (size_t i = 0; i < m->rows; i++) { + double delta = 0.0; + for (size_t j = 0; j < m->cols; j++) { + if (i == j) + continue; + delta += m->data[i]->data[j] * x_k_1->data[j]; + } + x_k_1->data[i] = (b->data[i] - delta) / m->data[i]->data[i]; + } + + if (l2_distance(x_k_1, x_k) <= l2_convergence_tolerance) + break; + } + + free_vector(x_k); + return x_k_1; +} + +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; +} + +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; +} + +void free_matrix(Matrix_double *m) { + for (size_t y = 0; y < m->rows; ++y) + free_vector(m->data[y]); + free(m); +} + +void format_matrix_into(Matrix_double *m, char *s) { + if (m->rows == 0) + strcpy(s, "empty"); + + for (size_t y = 0; y < m->rows; ++y) { + char row_s[5192]; + strcpy(row_s, ""); + + format_vector_into(m->data[y], row_s); + strcat(s, row_s); + } + strcat(s, "\n"); +} + +int matrix_equal(Matrix_double *a, Matrix_double *b) { + if (a->cols != b->cols || a->rows != b->rows) + return false; + + for (size_t y = 0; y < a->rows; ++y) + if (!vector_equal(a->data[y], b->data[y])) { + return false; + } + return true; +} diff --git a/Homework/math4610/src/rand.c b/Homework/math4610/src/rand.c new file mode 100644 index 0000000..574a955 --- /dev/null +++ b/Homework/math4610/src/rand.c @@ -0,0 +1,5 @@ +#include "lizfcm.h" + +double rand_from(double min, double max) { + return min + (rand() / (RAND_MAX / (max - min))); +} diff --git a/Homework/math4610/src/roots.c b/Homework/math4610/src/roots.c new file mode 100644 index 0000000..d6b22af --- /dev/null +++ b/Homework/math4610/src/roots.c @@ -0,0 +1,127 @@ +#include "lizfcm.h" +#include <assert.h> +#include <math.h> + +// f is well defined at start_x + delta*n for all n on the integer range [0, +// max_iterations] +Array_double *find_ivt_range(double (*f)(double), double start_x, double delta, + size_t max_iterations) { + double a = start_x; + + while (f(a) * f(a + delta) >= 0 && max_iterations > 0) { + max_iterations--; + a += delta; + } + + double end = a + delta; + double begin = a - delta; + + if (max_iterations == 0 && f(begin) * f(end) >= 0) + return NULL; + return InitArray(double, {begin, end}); +} + +// f is continuous on [a, b] +Array_double *bisect_find_root(double (*f)(double), double a, double b, + double tolerance, size_t max_iterations) { + assert(a <= b); + // guarantee there's a root somewhere between a and b by IVT + assert(f(a) * f(b) < 0); + + double c = (1.0 / 2) * (a + b); + if (b - a < tolerance || max_iterations == 0) + return InitArray(double, {a, b, c}); + + if (f(a) * f(c) < 0) + return bisect_find_root(f, a, c, tolerance, max_iterations - 1); + return bisect_find_root(f, c, b, tolerance, max_iterations - 1); +} + +double bisect_find_root_with_error_assumption(double (*f)(double), double a, + double b, double tolerance) { + assert(a <= b); + + uint64_t max_iterations = + (uint64_t)ceil((log(tolerance) - log(b - a)) / log(1 / 2.0)); + + Array_double *a_b_root = bisect_find_root(f, a, b, tolerance, max_iterations); + double root = a_b_root->data[2]; + free_vector(a_b_root); + + return root; +} + +double fixed_point_iteration_method(double (*f)(double), double (*g)(double), + double x_0, double tolerance, + size_t max_iterations) { + if (max_iterations <= 0) + return x_0; + + double root = g(x_0); + if (tolerance >= fabs(f(root))) + return root; + + return fixed_point_iteration_method(f, g, root, tolerance, + max_iterations - 1); +} + +double fixed_point_newton_method(double (*f)(double), double (*fprime)(double), + double x_0, double tolerance, + size_t max_iterations) { + if (max_iterations <= 0) + return x_0; + + double root = x_0 - f(x_0) / fprime(x_0); + if (tolerance >= fabs(f(root))) + return root; + + return fixed_point_newton_method(f, fprime, root, tolerance, + max_iterations - 1); +} + +double fixed_point_secant_method(double (*f)(double), double x_0, double x_1, + double tolerance, size_t max_iterations) { + if (max_iterations == 0) + return x_1; + + double root = x_1 - f(x_1) * ((x_1 - x_0) / (f(x_1) - f(x_0))); + + if (tolerance >= fabs(f(root))) + return root; + + return fixed_point_secant_method(f, x_1, root, tolerance, max_iterations - 1); +} + +double fixed_point_secant_bisection_method(double (*f)(double), double x_0, + double x_1, double tolerance, + size_t max_iterations) { + double begin = x_0; + double end = x_1; + double root = x_0; + + while (tolerance < fabs(f(root)) && max_iterations > 0) { + max_iterations--; + + double secant_root = fixed_point_secant_method(f, begin, end, tolerance, 1); + + if (secant_root < begin || secant_root > end) { + Array_double *range_root = bisect_find_root(f, begin, end, tolerance, 1); + + begin = range_root->data[0]; + end = range_root->data[1]; + root = range_root->data[2]; + + free_vector(range_root); + continue; + } + + root = secant_root; + + if (f(root) * f(begin) < 0) + end = secant_root; // the root exists in [begin, secant_root] + else + begin = secant_root; + } + + return root; +} diff --git a/Homework/math4610/src/vector.c b/Homework/math4610/src/vector.c new file mode 100644 index 0000000..1b3e0b0 --- /dev/null +++ b/Homework/math4610/src/vector.c @@ -0,0 +1,143 @@ +#include "lizfcm.h" +#include <assert.h> +#include <float.h> +#include <math.h> +#include <stdio.h> +#include <string.h> + +Array_double *add_v(Array_double *v1, Array_double *v2) { + assert(v1->size == v2->size); + + Array_double *sum = copy_vector(v1); + for (size_t i = 0; i < v1->size; i++) + sum->data[i] += v2->data[i]; + return sum; +} + +Array_double *minus_v(Array_double *v1, Array_double *v2) { + assert(v1->size == v2->size); + + Array_double *sub = InitArrayWithSize(double, v1->size, 0); + for (size_t i = 0; i < v1->size; i++) + sub->data[i] = v1->data[i] - v2->data[i]; + return sub; +} + +Array_double *scale_v(Array_double *v, double m) { + Array_double *copy = copy_vector(v); + for (size_t i = 0; i < v->size; i++) + copy->data[i] *= m; + return copy; +} + +double l1_norm(Array_double *v) { + double sum = 0; + for (size_t i = 0; i < v->size; ++i) + sum += fabs(v->data[i]); + return sum; +} + +double l2_norm(Array_double *v) { + double norm = 0; + for (size_t i = 0; i < v->size; ++i) + norm += v->data[i] * v->data[i]; + return sqrt(norm); +} + +double linf_norm(Array_double *v) { + assert(v->size > 0); + double max = v->data[0]; + for (size_t i = 0; i < v->size; ++i) + max = c_max(v->data[i], max); + return max; +} + +double v_dot_v(Array_double *v1, Array_double *v2) { + assert(v1->size == v2->size); + + double dot = 0; + for (size_t i = 0; i < v1->size; i++) + dot += v1->data[i] * v2->data[i]; + return dot; +} + +double vector_distance(Array_double *v1, Array_double *v2, + double (*norm)(Array_double *)) { + Array_double *minus = minus_v(v1, v2); + double dist = (*norm)(minus); + free(minus); + return dist; +} + +double l1_distance(Array_double *v1, Array_double *v2) { + return vector_distance(v1, v2, &l1_norm); +} + +double l2_distance(Array_double *v1, Array_double *v2) { + return vector_distance(v1, v2, &l2_norm); +} + +double linf_distance(Array_double *v1, Array_double *v2) { + return vector_distance(v1, v2, &linf_norm); +} + +Array_double *copy_vector(Array_double *v) { + Array_double *copy = InitArrayWithSize(double, v->size, 0.0); + for (size_t i = 0; i < copy->size; ++i) + copy->data[i] = v->data[i]; + return copy; +} + +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; +} + +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; +} + +void free_vector(Array_double *v) { + free(v->data); + free(v); +} + +void format_vector_into(Array_double *v, char *s) { + if (v->size == 0) { + strcat(s, "empty"); + return; + } + + for (size_t i = 0; i < v->size; ++i) { + char num[64]; + strcpy(num, ""); + + sprintf(num, "%f,", v->data[i]); + strcat(s, num); + } + strcat(s, "\n"); +} + +double sum_v(Array_double *v) { + double sum = 0; + for (size_t i = 0; i < v->size; i++) + sum += v->data[i]; + return sum; +} + +int vector_equal(Array_double *a, Array_double *b) { + if (a->size != b->size) + return false; + + for (size_t i = 0; i < a->size; ++i) { + if (a->data[i] != b->data[i]) + return false; + } + return true; +} diff --git a/Homework/math4610/test/approx_derivative.t.c b/Homework/math4610/test/approx_derivative.t.c new file mode 100644 index 0000000..6bcac79 --- /dev/null +++ b/Homework/math4610/test/approx_derivative.t.c @@ -0,0 +1,32 @@ +#include "lizfcm.test.h" + +double f(double x) { return x * x; } + +double f_prime(double x) { return 2 * x; } + +double H = 0.0001; +double ACCEPTED_DERIVATIVE_ERROR = 0.0001; + +UTEST(derivative, central) { + double a = 3.0; + double expected = f_prime(a); + double f_prime_x = central_derivative_at(&f, a, H); + + EXPECT_NEAR(expected, f_prime_x, ACCEPTED_DERIVATIVE_ERROR); +} + +UTEST(derivative, forward) { + double a = 3.0; + double expected = f_prime(a); + double f_prime_x = forward_derivative_at(&f, a, H); + + EXPECT_NEAR(expected, f_prime_x, ACCEPTED_DERIVATIVE_ERROR); +} + +UTEST(derivative, backward) { + double a = 3.0; + double expected = f_prime(a); + double f_prime_x = backward_derivative_at(&f, a, H); + + EXPECT_NEAR(expected, f_prime_x, ACCEPTED_DERIVATIVE_ERROR); +} diff --git a/Homework/math4610/test/eigen.t.c b/Homework/math4610/test/eigen.t.c new file mode 100644 index 0000000..dc01aa7 --- /dev/null +++ b/Homework/math4610/test/eigen.t.c @@ -0,0 +1,147 @@ +#include "lizfcm.test.h" +#include <math.h> + +Matrix_double *eigen_test_matrix() { + // produces a matrix that has eigenvalues [5 + sqrt{17}, 2, 5 - sqrt{17}] + Matrix_double *m = InitMatrixWithSize(double, 3, 3, 0.0); + m->data[0]->data[0] = 2.0; + m->data[0]->data[1] = 2.0; + m->data[0]->data[2] = 4.0; + m->data[1]->data[0] = 1.0; + m->data[1]->data[1] = 4.0; + m->data[1]->data[2] = 7.0; + m->data[2]->data[1] = 2.0; + m->data[2]->data[2] = 6.0; + return m; +} + +UTEST(eigen, least_dominant_eigenvalue) { + Matrix_double *m = eigen_test_matrix(); + + double expected_least_dominant_eigenvalue = 0.87689; // 5 - sqrt(17) + double tolerance = 0.0001; + uint64_t max_iterations = 64; + + Array_double *v_guess = InitArrayWithSize(double, 3, 1.0); + double approx_least_dominant_eigenvalue = + least_dominant_eigenvalue(m, v_guess, tolerance, max_iterations); + + EXPECT_NEAR(expected_least_dominant_eigenvalue, + approx_least_dominant_eigenvalue, tolerance); +} + +UTEST(eigen, dominant_eigenvalue) { + Matrix_double *m = InitMatrixWithSize(double, 2, 2, 0.0); + m->data[0]->data[0] = 2.0; + m->data[0]->data[1] = -12.0; + m->data[1]->data[0] = 1.0; + m->data[1]->data[1] = -5.0; + + Array_double *v_guess = InitArrayWithSize(double, 2, 1.0); + double tolerance = 0.0001; + uint64_t max_iterations = 64; + + double expect_dominant_eigenvalue = -2.0; + + double approx_dominant_eigenvalue = + dominant_eigenvalue(m, v_guess, tolerance, max_iterations); + + EXPECT_NEAR(expect_dominant_eigenvalue, approx_dominant_eigenvalue, + tolerance); + free_matrix(m); + free_vector(v_guess); +} + +UTEST(eigen, shifted_eigenvalue) { + Matrix_double *m = eigen_test_matrix(); + + double least_dominant_eigenvalue = 0.87689; // 5 - sqrt{17} + double dominant_eigenvalue = 9.12311; // 5 + sqrt{17} + double expected_middle_eigenvalue = 2.0; + double shift = (dominant_eigenvalue + least_dominant_eigenvalue) / 2.0; + + double tolerance = 0.0001; + uint64_t max_iterations = 64; + Array_double *v_guess = InitArray(double, {0.5, 1.0, 0.75}); + + double approx_middle_eigenvalue = shift_inverse_power_eigenvalue( + m, v_guess, shift, tolerance, max_iterations); + + EXPECT_NEAR(approx_middle_eigenvalue, expected_middle_eigenvalue, tolerance); +} + +UTEST(eigen, partition_find_eigenvalues) { + Matrix_double *m = eigen_test_matrix(); + + double least_dominant_eigenvalue = 0.87689; // 5 - sqrt{17} + double dominant_eigenvalue = 9.12311; // 5 + sqrt{17} + double expected_middle_eigenvalue = 2.0; + double expected_eigenvalues[3] = {least_dominant_eigenvalue, + expected_middle_eigenvalue, + dominant_eigenvalue}; + + size_t partitions = 10; + Matrix_double *guesses = InitMatrixWithSize(double, partitions, 3, 0.0); + for (size_t y = 0; y < guesses->rows; y++) { + free_vector(guesses->data[y]); + guesses->data[y] = InitArray(double, {0.5, 1.0, 0.75}); + } + + double tolerance = 0.0001; + uint64_t max_iterations = 64; + + int eigenvalues_found[3] = {false, false, false}; + Array_double *partition_eigenvalues = + partition_find_eigenvalues(m, guesses, tolerance, max_iterations); + + for (size_t i = 0; i < partition_eigenvalues->size; i++) + for (size_t eigenvalue_i = 0; eigenvalue_i < 3; eigenvalue_i++) + if (fabs(partition_eigenvalues->data[i] - expected_eigenvalues[i]) <= + tolerance) + eigenvalues_found[eigenvalue_i] = true; + + for (size_t eigenvalue_i = 0; eigenvalue_i < 3; eigenvalue_i++) + EXPECT_TRUE(eigenvalues_found[eigenvalue_i]); +} + +UTEST(eigen, leslie_matrix_dominant_eigenvalue) { + Array_double *felicity = InitArray(double, {0.0, 1.5, 0.8}); + Array_double *survivor_ratios = InitArray(double, {0.8, 0.55}); + Matrix_double *leslie = leslie_matrix(survivor_ratios, felicity); + Array_double *v_guess = InitArrayWithSize(double, 3, 2.0); + double tolerance = 0.0001; + uint64_t max_iterations = 64; + + double expect_dominant_eigenvalue = 1.22005; + + double approx_dominant_eigenvalue = + dominant_eigenvalue(leslie, v_guess, tolerance, max_iterations); + + EXPECT_NEAR(expect_dominant_eigenvalue, approx_dominant_eigenvalue, + tolerance); + + free_vector(v_guess); + free_vector(survivor_ratios); + free_vector(felicity); + free_matrix(leslie); +} +UTEST(eigen, leslie_matrix) { + Array_double *felicity = InitArray(double, {0.0, 1.5, 0.8}); + Array_double *survivor_ratios = InitArray(double, {0.8, 0.55}); + + Matrix_double *m = InitMatrixWithSize(double, 3, 3, 0.0); + m->data[0]->data[0] = 0.0; + m->data[0]->data[1] = 1.5; + m->data[0]->data[2] = 0.8; + m->data[1]->data[0] = 0.8; + m->data[2]->data[1] = 0.55; + + Matrix_double *leslie = leslie_matrix(survivor_ratios, felicity); + + EXPECT_TRUE(matrix_equal(leslie, m)); + + free_matrix(leslie); + free_matrix(m); + free_vector(felicity); + free_vector(survivor_ratios); +} diff --git a/Homework/math4610/test/jacobi.t.c b/Homework/math4610/test/jacobi.t.c new file mode 100644 index 0000000..94ed53a --- /dev/null +++ b/Homework/math4610/test/jacobi.t.c @@ -0,0 +1,93 @@ +#include "lizfcm.test.h" +#include <assert.h> +#include <math.h> + +Matrix_double *generate_ddm(size_t n) { + Matrix_double *m = InitMatrixWithSize(double, n, n, rand_from(0.0, 1.0)); + + for (size_t y = 0; y < m->rows; y++) { + m->data[y]->data[y] += sum_v(m->data[y]); + } + + return m; +} + +UTEST(jacobi, jacobi_solve) { + Matrix_double *m = generate_ddm(2); + + Array_double *b_1 = InitArrayWithSize(double, m->rows, 1.0); + Array_double *b = m_dot_v(m, b_1); + + double tolerance = 0.001; + size_t max_iter = 400; + Array_double *solution = jacobi_solve(m, b, tolerance, max_iter); + + for (size_t y = 0; y < m->rows; y++) { + double dot = v_dot_v(m->data[y], solution); + EXPECT_NEAR(b->data[y], dot, 0.1); + } + + free_matrix(m); + free_vector(b_1); + free_vector(b); + free_vector(solution); +} + +UTEST(jacobi, gauss_siedel_solve) { + Matrix_double *m = generate_ddm(2); + + Array_double *b_1 = InitArrayWithSize(double, m->rows, 1.0); + Array_double *b = m_dot_v(m, b_1); + + double tolerance = 0.001; + size_t max_iter = 400; + Array_double *solution = gauss_siedel_solve(m, b, tolerance, max_iter); + + for (size_t y = 0; y < m->rows; y++) { + double dot = v_dot_v(m->data[y], solution); + EXPECT_NEAR(b->data[y], dot, 0.1); + } + + free_matrix(m); + free_vector(b_1); + free_vector(b); + free_vector(solution); +} + +UTEST(jacobi, leslie_solve) { + Array_double *felicity = InitArray(double, {0.0, 1.5, 0.8}); + Array_double *survivor_ratios = InitArray(double, {0.8, 0.55}); + Matrix_double *leslie = leslie_matrix(survivor_ratios, felicity); + + Array_double *initial_pop = InitArray(double, {10.0, 20.0, 15.0}); + Array_double *next = m_dot_v(leslie, initial_pop); + + Matrix_double *augmented = add_column(leslie, next); + Matrix_double *leslie_augmented_echelon = gaussian_elimination(augmented); + + Array_double *next_echelon = + col_v(leslie_augmented_echelon, leslie_augmented_echelon->cols - 1); + Matrix_double *leslie_echelon = slice_column( + leslie_augmented_echelon, leslie_augmented_echelon->cols - 1); + + double tolerance = 0.001; + size_t max_iter = 400; + Array_double *initial_pop_guess = + jacobi_solve(leslie_echelon, next_echelon, tolerance, max_iter); + + for (size_t y = 0; y < initial_pop->size; y++) { + EXPECT_NEAR(initial_pop_guess->data[y], initial_pop->data[y], 0.05); + } + + free_matrix(leslie); + free_matrix(augmented); + free_matrix(leslie_augmented_echelon); + free_matrix(leslie_echelon); + + free_vector(felicity); + free_vector(survivor_ratios); + free_vector(next); + free_vector(next_echelon); + free_vector(initial_pop); + free_vector(initial_pop_guess); +} diff --git a/Homework/math4610/test/lin.t.c b/Homework/math4610/test/lin.t.c new file mode 100644 index 0000000..6ad1613 --- /dev/null +++ b/Homework/math4610/test/lin.t.c @@ -0,0 +1,20 @@ +#include "lizfcm.test.h" + +UTEST(Lin, least_squares_lin_reg_perfect) { + Array_double *x = InitArray(double, {0, 1, 2, 3, 4}); + Array_double *y = InitArray(double, {1, 2, 3, 4, 5}); + + Line *line = least_squares_lin_reg(x, y); + + EXPECT_EQ(line->m, 1.0); + EXPECT_EQ(line->a, 1.0); +} + +UTEST(Lin, least_squares_lin_reg_estimate) { + Array_double *x = InitArray(double, {1, 2, 3, 4, 5, 6, 7}); + Array_double *y = InitArray(double, {0.5, 3, 2, 3.5, 5, 6, 7.5}); + + Line *line = least_squares_lin_reg(x, y); + + EXPECT_NEAR(line->m, 1.0, 0.2); +} diff --git a/Homework/math4610/test/lizfcm.test.h b/Homework/math4610/test/lizfcm.test.h new file mode 100644 index 0000000..9819d46 --- /dev/null +++ b/Homework/math4610/test/lizfcm.test.h @@ -0,0 +1,8 @@ +#ifndef LIZFCM_TEST_H +#define LIZFCM_TEST_H + +#include "lizfcm.h" + +#include "utest.h" + +#endif // LIZFCM_TEST_H diff --git a/Homework/math4610/test/maceps.t.c b/Homework/math4610/test/maceps.t.c new file mode 100644 index 0000000..9c45659 --- /dev/null +++ b/Homework/math4610/test/maceps.t.c @@ -0,0 +1,18 @@ +#include "lizfcm.test.h" +#include <math.h> + +UTEST(maceps, smaceps) { + float epsilon = smaceps(); + float one = 1.0; + float approx_one = one + epsilon; + + EXPECT_LE(approx_one - one, 0); +} + +UTEST(maceps, dmaceps) { + double epsilon = dmaceps(); + double one = 1.0; + double approx_one = one + epsilon; + + EXPECT_LE(approx_one - one, 0); +} diff --git a/Homework/math4610/test/main.c b/Homework/math4610/test/main.c new file mode 100644 index 0000000..6a92d4a --- /dev/null +++ b/Homework/math4610/test/main.c @@ -0,0 +1,12 @@ +#include "lizfcm.test.h" +#include <stdlib.h> +#include <time.h> + +UTEST(basic, unit_tests) { ASSERT_TRUE(1); } + +UTEST_STATE(); +int main(int argc, const char *const argv[]) { + srand(time(NULL)); + + return utest_main(argc, argv); +} diff --git a/Homework/math4610/test/matrix.t.c b/Homework/math4610/test/matrix.t.c new file mode 100644 index 0000000..597d6e0 --- /dev/null +++ b/Homework/math4610/test/matrix.t.c @@ -0,0 +1,247 @@ +#include "lizfcm.test.h" + +UTEST(matrix, free) { + Matrix_double *m = InitMatrixWithSize(double, 8, 8, 0.0); + uint64_t data_addr = (uint64_t)(m->data); + free_matrix(m); + EXPECT_NE(data_addr, (uint64_t)(m->data)); +} + +UTEST(matrix, add_column) { + Matrix_double *m = InitMatrixWithSize(double, 5, 5, 0.0); + Array_double *col = InitArray(double, {1.0, 2.0, 3.0, 4.0, 5.0}); + Matrix_double *new_m = add_column(m, col); + + for (size_t row = 0; row < m->rows; row++) + EXPECT_EQ(new_m->data[row]->data[m->cols], col->data[row]); + EXPECT_EQ(new_m->cols, m->cols + 1); + + free_matrix(m); + free_matrix(new_m); + free_vector(col); +} + +UTEST(matrix, slice_column) { + size_t slice = 1; + + Matrix_double *m = InitMatrixWithSize(double, 5, 5, 1.0 * (rand() % 10)); + Matrix_double *new_m = slice_column(m, slice); + + for (size_t row = 0; row < m->rows; row++) { + Array_double *sliced_row = slice_element(m->data[row], slice); + + EXPECT_TRUE(vector_equal(new_m->data[row], sliced_row)); + free_vector(sliced_row); + } + EXPECT_EQ(new_m->cols, m->cols - 1); + + free_matrix(m); + free_matrix(new_m); +} + +UTEST(matrix, put_identity_diagonal) { + Matrix_double *m = InitMatrixWithSize(double, 8, 8, 0.0); + Matrix_double *ident = put_identity_diagonal(m); + + for (size_t y = 0; y < m->rows; ++y) + for (size_t x = 0; x < m->cols; ++x) + EXPECT_EQ(ident->data[y]->data[x], x == y ? 1.0 : 0.0); + + free_matrix(m); + free_matrix(ident); +} + +UTEST(matrix, copy) { + Matrix_double *m = InitMatrixWithSize(double, 8, 8, 0.0); + Matrix_double *ident = put_identity_diagonal(m); + + Matrix_double *copy = copy_matrix(ident); + + EXPECT_TRUE(matrix_equal(ident, copy)); + + free_matrix(m); + free_matrix(ident); + free_matrix(copy); +} + +UTEST(matrix, m_dot_v) { + Matrix_double *m = InitMatrixWithSize(double, 8, 8, 0.0); + Matrix_double *ident = put_identity_diagonal(m); + + Array_double *x = InitArray(double, {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0}); + Array_double *dotted = m_dot_v(ident, x); + + EXPECT_TRUE(vector_equal(dotted, x)); + + free_matrix(m); + free_matrix(ident); + free_vector(x); + free_vector(dotted); +} + +UTEST(matrix, bsubst) { + Matrix_double *u = InitMatrixWithSize(double, 3, 3, 0.0); + u->data[0]->data[0] = 1.0; + u->data[0]->data[1] = 2.0; + u->data[0]->data[2] = 3.0; + u->data[1]->data[1] = 4.0; + u->data[1]->data[2] = 5.0; + u->data[2]->data[2] = 6.0; + + Array_double *b = InitArray(double, {14.0, 29.0, 30.0}); + + Array_double *solution = bsubst(u, b); + EXPECT_NEAR(solution->data[0], -3.0, 0.0001); + EXPECT_NEAR(solution->data[1], 1.0, 0.0001); + EXPECT_NEAR(solution->data[2], 5.0, 0.0001); + + free_matrix(u); + free_vector(b); + free_vector(solution); +} + +UTEST(matrix, fsubst) { + Matrix_double *l = InitMatrixWithSize(double, 3, 3, 0.0); + l->data[0]->data[0] = 1.0; + l->data[1]->data[0] = 2.0; + l->data[1]->data[1] = 3.0; + l->data[2]->data[0] = 4.0; + l->data[2]->data[1] = 5.0; + l->data[2]->data[2] = 6.0; + + Array_double *b = InitArray(double, {14.0, 13.0, 32.0}); + + Array_double *solution = fsubst(l, b); + EXPECT_NEAR(solution->data[0], 14.0, 0.0001); + EXPECT_NEAR(solution->data[1], -5.0, 0.0001); + EXPECT_NEAR(solution->data[2], 0.16667, 0.0001); + + free_matrix(l); + free_vector(b); + free_vector(solution); +} + +UTEST(matrix, lu_decomp) { + Matrix_double *m = InitMatrixWithSize(double, 10, 10, 0.0); + for (size_t y = 0; y < m->rows; ++y) { + for (size_t x = 0; x < m->cols; ++x) + m->data[y]->data[x] = x == y ? 20.0 : (100.0 - rand() % 100) / 100.0; + } + + Matrix_double **ul = lu_decomp(m); + Matrix_double *u = ul[0]; + Matrix_double *l = ul[1]; + for (int y = 0; y < m->rows; y++) { + for (size_t x = 0; x < c_max(y - 1, 0); x++) { + double u_yx = u->data[y]->data[x]; + EXPECT_NEAR(u_yx, 0.0, 0.0001); + } + + for (size_t x = c_min(m->cols, y + 1); x < m->cols; ++x) { + double l_yx = l->data[y]->data[x]; + EXPECT_NEAR(l_yx, 0.0, 0.0001); + } + } + + free_matrix(m); + free_matrix(l); + free_matrix(u); + free(ul); +} + +UTEST(matrix, solve_gaussian_elimination) { + Matrix_double *m = InitMatrixWithSize(double, 10, 10, 0.0); + for (size_t y = 0; y < m->rows; ++y) { + for (size_t x = 0; x < m->cols; ++x) + m->data[y]->data[x] = x == y ? 20.0 : (100.0 - rand() % 100) / 100.0; + } + + Array_double *b_1 = InitArrayWithSize(double, m->rows, 1.0); + Array_double *b = m_dot_v(m, b_1); + + Array_double *solution = solve_matrix_gaussian(m, b); + + for (size_t y = 0; y < m->rows; y++) { + double dot = v_dot_v(m->data[y], solution); + EXPECT_NEAR(b->data[y], dot, 0.0001); + } + + free_vector(b_1); + free_matrix(m); + free_vector(b); + free_vector(solution); +} + +UTEST(matrix, solve_matrix_lu_bsubst) { + Matrix_double *m = InitMatrixWithSize(double, 10, 10, 0.0); + for (size_t y = 0; y < m->rows; ++y) { + for (size_t x = 0; x < m->cols; ++x) + m->data[y]->data[x] = x == y ? 20.0 : (100.0 - rand() % 100) / 100.0; + } + + Array_double *b_1 = InitArrayWithSize(double, m->rows, 1.0); + Array_double *b = m_dot_v(m, b_1); + + Array_double *solution = solve_matrix_lu_bsubst(m, b); + + for (size_t y = 0; y < m->rows; y++) { + double dot = v_dot_v(m->data[y], solution); + EXPECT_NEAR(b->data[y], dot, 0.0001); + } + + free_matrix(m); + free_vector(b); + free_vector(b_1); + free_vector(solution); +} + +UTEST(matrix, col_v) { + Matrix_double *m = InitMatrixWithSize(double, 2, 3, 0.0); + // set element to its column index + for (size_t y = 0; y < m->rows; y++) { + for (size_t x = 0; x < m->cols; x++) { + m->data[y]->data[x] = x; + } + } + + Array_double *col, *expected; + for (size_t x = 0; x < m->cols; x++) { + col = col_v(m, x); + expected = InitArrayWithSize(double, m->rows, (double)x); + EXPECT_TRUE(vector_equal(expected, col)); + free_vector(col); + free_vector(expected); + } + + free_matrix(m); +} + +UTEST(matrix, m_dot_m) { + Matrix_double *a = InitMatrixWithSize(double, 1, 3, 12.0); + Matrix_double *b = InitMatrixWithSize(double, 3, 1, 10.0); + + Matrix_double *prod = m_dot_m(a, b); + + EXPECT_EQ(prod->cols, 1); + EXPECT_EQ(prod->rows, 1); + EXPECT_EQ(12.0 * 10.0 * 3, prod->data[0]->data[0]); + + free_matrix(a); + free_matrix(b); + free_matrix(prod); +} + +UTEST(matrix, transpose) { + Matrix_double *a = InitMatrixWithSize(double, 1, 3, 12.0); + a->data[0]->data[1] = 13.0; + Matrix_double *b = InitMatrixWithSize(double, 3, 1, 12.0); + b->data[1]->data[0] = 13.0; + + Matrix_double *a_t = transpose(a); + + EXPECT_TRUE(matrix_equal(a_t, b)); + + free_matrix(a_t); + free_matrix(a); + free_matrix(b); +} diff --git a/Homework/math4610/test/rand.t.c b/Homework/math4610/test/rand.t.c new file mode 100644 index 0000000..9ed2e9c --- /dev/null +++ b/Homework/math4610/test/rand.t.c @@ -0,0 +1,10 @@ +#include "lizfcm.test.h" + +UTEST(rand, rand_from) { + double min = -2.0; + double max = 5.0; + for (size_t i = 0; i < 1000; i++) { + double r = rand_from(min, max); + ASSERT_TRUE(min <= r && r <= max); + } +} diff --git a/Homework/math4610/test/roots.t.c b/Homework/math4610/test/roots.t.c new file mode 100644 index 0000000..c20f22e --- /dev/null +++ b/Homework/math4610/test/roots.t.c @@ -0,0 +1,114 @@ +#include "lizfcm.test.h" +#include <math.h> +#include <stdio.h> + +double f1(double x) { return x * x - 9; } + +double f2(double x) { return x * x - 5 * x + 6; } +double f2prime(double x) { return 2 * x - 5; } +double g1(double x) { return x + f2(x); } +double g2(double x) { return x - f2(x); } + +UTEST(ivt, find_interval) { + Array_double *range = find_ivt_range(&f1, -10.0, 0.10, 200); + EXPECT_LT(f1(range->data[0]) * f1(range->data[1]), 0); + + free_vector(range); +} + +UTEST(root, bisection_with_error_assumption) { + Array_double *range = find_ivt_range(&f2, 2.5, 0.10, 200); + + double tolerance = 0.01; + double root1 = bisect_find_root_with_error_assumption( + &f2, range->data[0], range->data[1], tolerance); + + free_vector(range); + range = find_ivt_range(&f2, 0, 0.01, 500); + double root2 = bisect_find_root_with_error_assumption( + &f2, range->data[0], range->data[1], tolerance); + free_vector(range); + + EXPECT_NEAR(3.0, root1, tolerance); + EXPECT_NEAR(2.0, root2, tolerance); +} + +UTEST(root, fixed_point_iteration_method) { + // x^2 - 5x + 6 = (x - 3)(x - 2) + double expect_x2 = 3.0; + double expect_x1 = 2.0; + + double tolerance = 0.001; + uint64_t max_iterations = 10; + + double x_0 = 1.55; // 1.5 < 1.55 < 2.5 + // g1(x) = x + f(x) + double root1 = + fixed_point_iteration_method(&f2, &g1, x_0, tolerance, max_iterations); + EXPECT_NEAR(root1, expect_x1, tolerance); + + // g2(x) = x - f(x) + x_0 = 3.4; // 2.5 < 3.4 < 3.5 + double root2 = + fixed_point_iteration_method(&f2, &g2, x_0, tolerance, max_iterations); + EXPECT_NEAR(root2, expect_x2, tolerance); +} + +UTEST(root, fixed_point_newton_method) { + // x^2 - 5x + 6 = (x - 3)(x - 2) + double expect_x2 = 3.0; + double expect_x1 = 2.0; + + double tolerance = 0.01; + uint64_t max_iterations = 10; + + double x_0 = 1.55; // 1.5 < 1.55 < 2.5 + double root1 = + fixed_point_newton_method(&f2, &f2prime, x_0, tolerance, max_iterations); + EXPECT_NEAR(root1, expect_x1, tolerance); + + x_0 = 3.4; // 2.5 < 3.4 < 3.5 + double root2 = + fixed_point_newton_method(&f2, &f2prime, x_0, tolerance, max_iterations); + EXPECT_NEAR(root2, expect_x2, tolerance); +} + +UTEST(root, fixed_point_secant_method) { + // x^2 - 5x + 6 = (x - 3)(x - 2) + double expect_x2 = 3.0; + double expect_x1 = 2.0; + + double delta = 0.01; + double tolerance = 0.01; + uint64_t max_iterations = 10; + + double x_0 = 1.55; // 1.5 < 1.55 < 2.5 + double root1 = fixed_point_secant_method(&f2, x_0, x_0 + delta, tolerance, + max_iterations); + EXPECT_NEAR(root1, expect_x1, tolerance); + + x_0 = 3.4; // 2.5 < 3.4 < 3.5 + double root2 = fixed_point_secant_method(&f2, x_0, x_0 + delta, tolerance, + max_iterations); + EXPECT_NEAR(root2, expect_x2, tolerance); +} + +UTEST(root, fixed_point_hybrid_method) { + // x^2 - 5x + 6 = (x - 3)(x - 2) + double expect_x2 = 3.0; + double expect_x1 = 2.0; + + double delta = 1.0; + double tolerance = 0.01; + uint64_t max_iterations = 10; + + double x_0 = 1.55; + double root1 = fixed_point_secant_bisection_method(&f2, x_0, x_0 + delta, + tolerance, max_iterations); + EXPECT_NEAR(root1, expect_x1, tolerance); + + x_0 = 2.5; + double root2 = fixed_point_secant_bisection_method(&f2, x_0, x_0 + delta, + tolerance, max_iterations); + EXPECT_NEAR(root2, expect_x2, tolerance); +} diff --git a/Homework/math4610/test/utest.h b/Homework/math4610/test/utest.h new file mode 100644 index 0000000..8767600 --- /dev/null +++ b/Homework/math4610/test/utest.h @@ -0,0 +1,1668 @@ +/* + The latest version of this library is available on GitHub; + https://github.com/sheredom/utest.h +*/ + +/* + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + For more information, please refer to <http://unlicense.org/> +*/ + +#ifndef SHEREDOM_UTEST_H_INCLUDED +#define SHEREDOM_UTEST_H_INCLUDED + +#ifdef _MSC_VER +/* + Disable warning about not inlining 'inline' functions. +*/ +#pragma warning(disable : 4710) + +/* + Disable warning about inlining functions that are not marked 'inline'. +*/ +#pragma warning(disable : 4711) + +/* + Disable warning for alignment padding added +*/ +#pragma warning(disable : 4820) + +#if _MSC_VER > 1900 +/* + Disable warning about preprocessor macros not being defined in MSVC headers. +*/ +#pragma warning(disable : 4668) + +/* + Disable warning about no function prototype given in MSVC headers. +*/ +#pragma warning(disable : 4255) + +/* + Disable warning about pointer or reference to potentially throwing function. +*/ +#pragma warning(disable : 5039) + +/* + Disable warning about macro expansion producing 'defined' has undefined + behavior. +*/ +#pragma warning(disable : 5105) +#endif + +#if _MSC_VER > 1930 +/* + Disable warning about 'const' variable is not used. +*/ +#pragma warning(disable : 5264) +#endif + +#pragma warning(push, 1) +#endif + +#if defined(_MSC_VER) && (_MSC_VER < 1920) +typedef __int64 utest_int64_t; +typedef unsigned __int64 utest_uint64_t; +typedef unsigned __int32 utest_uint32_t; +#else +#include <stdint.h> +typedef int64_t utest_int64_t; +typedef uint64_t utest_uint64_t; +typedef uint32_t utest_uint32_t; +#endif + +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#if defined(__cplusplus) +#if defined(_MSC_VER) && !defined(_CPPUNWIND) +/* We're on MSVC and the compiler is compiling without exception support! */ +#elif !defined(_MSC_VER) && !defined(__EXCEPTIONS) +/* We're on a GCC/Clang compiler that doesn't have exception support! */ +#else +#define UTEST_HAS_EXCEPTIONS 1 +#endif +#endif + +#if defined(UTEST_HAS_EXCEPTIONS) +#include <stdexcept> +#endif + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +#if defined(__cplusplus) +#define UTEST_C_FUNC extern "C" +#else +#define UTEST_C_FUNC +#endif + +#define UTEST_TEST_PASSED (0) +#define UTEST_TEST_FAILURE (1) +#define UTEST_TEST_SKIPPED (2) + +#if defined(__TINYC__) +#define UTEST_ATTRIBUTE(a) __attribute((a)) +#else +#define UTEST_ATTRIBUTE(a) __attribute__((a)) +#endif + +#if defined(_MSC_VER) || defined(__MINGW64__) || defined(__MINGW32__) + +#if defined(__MINGW64__) || defined(__MINGW32__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpragmas" +#pragma GCC diagnostic ignored "-Wunknown-pragmas" +#endif + +#if defined(_WINDOWS_) || defined(_WINDOWS_H) +typedef LARGE_INTEGER utest_large_integer; +#else +// use old QueryPerformanceCounter definitions (not sure is this needed in some +// edge cases or not) on Win7 with VS2015 these extern declaration cause "second +// C linkage of overloaded function not allowed" error +typedef union { + struct { + unsigned long LowPart; + long HighPart; + } DUMMYSTRUCTNAME; + struct { + unsigned long LowPart; + long HighPart; + } u; + utest_int64_t QuadPart; +} utest_large_integer; + +UTEST_C_FUNC __declspec(dllimport) int __stdcall QueryPerformanceCounter( + utest_large_integer *); +UTEST_C_FUNC __declspec(dllimport) int __stdcall QueryPerformanceFrequency( + utest_large_integer *); + +#if defined(__MINGW64__) || defined(__MINGW32__) +#pragma GCC diagnostic pop +#endif +#endif + +#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \ + defined(__NetBSD__) || defined(__DragonFly__) || defined(__sun__) || \ + defined(__HAIKU__) +/* + slightly obscure include here - we need to include glibc's features.h, but + we don't want to just include a header that might not be defined for other + c libraries like musl. Instead we include limits.h, which we know on all + glibc distributions includes features.h +*/ +#include <limits.h> + +#if defined(__GLIBC__) && defined(__GLIBC_MINOR__) +#include <time.h> + +#if ((2 < __GLIBC__) || ((2 == __GLIBC__) && (17 <= __GLIBC_MINOR__))) +/* glibc is version 2.17 or above, so we can just use clock_gettime */ +#define UTEST_USE_CLOCKGETTIME +#else +#include <sys/syscall.h> +#include <unistd.h> +#endif +#else // Other libc implementations +#include <time.h> +#define UTEST_USE_CLOCKGETTIME +#endif + +#elif defined(__APPLE__) +#include <time.h> +#endif + +#if defined(_MSC_VER) && (_MSC_VER < 1920) +#define UTEST_PRId64 "I64d" +#define UTEST_PRIu64 "I64u" +#else +#include <inttypes.h> + +#define UTEST_PRId64 PRId64 +#define UTEST_PRIu64 PRIu64 +#endif + +#if defined(__cplusplus) +#define UTEST_INLINE inline + +#if defined(__clang__) +#define UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") + +#define UTEST_INITIALIZER_END_DISABLE_WARNINGS _Pragma("clang diagnostic pop") +#else +#define UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS +#define UTEST_INITIALIZER_END_DISABLE_WARNINGS +#endif + +#define UTEST_INITIALIZER(f) \ + struct f##_cpp_struct { \ + f##_cpp_struct(); \ + }; \ + UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS static f##_cpp_struct \ + f##_cpp_global UTEST_INITIALIZER_END_DISABLE_WARNINGS; \ + f##_cpp_struct::f##_cpp_struct() +#elif defined(_MSC_VER) +#define UTEST_INLINE __forceinline + +#if defined(_WIN64) +#define UTEST_SYMBOL_PREFIX +#else +#define UTEST_SYMBOL_PREFIX "_" +#endif + +#if defined(__clang__) +#define UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wmissing-variable-declarations\"") + +#define UTEST_INITIALIZER_END_DISABLE_WARNINGS _Pragma("clang diagnostic pop") +#else +#define UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS +#define UTEST_INITIALIZER_END_DISABLE_WARNINGS +#endif + +#pragma section(".CRT$XCU", read) +#define UTEST_INITIALIZER(f) \ + static void __cdecl f(void); \ + UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS \ + __pragma(comment(linker, "/include:" UTEST_SYMBOL_PREFIX #f "_")) \ + UTEST_C_FUNC __declspec(allocate(".CRT$XCU")) void(__cdecl * \ + f##_)(void) = f; \ + UTEST_INITIALIZER_END_DISABLE_WARNINGS \ + static void __cdecl f(void) +#else +#if defined(__linux__) +#if defined(__clang__) +#if __has_warning("-Wreserved-id-macro") +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif +#endif + +#define __STDC_FORMAT_MACROS 1 + +#if defined(__clang__) +#if __has_warning("-Wreserved-id-macro") +#pragma clang diagnostic pop +#endif +#endif +#endif + +#define UTEST_INLINE inline + +#define UTEST_INITIALIZER(f) \ + static void f(void) UTEST_ATTRIBUTE(constructor); \ + static void f(void) +#endif + +#if defined(__cplusplus) +#define UTEST_CAST(type, x) static_cast<type>(x) +#define UTEST_PTR_CAST(type, x) reinterpret_cast<type>(x) +#define UTEST_EXTERN extern "C" +#define UTEST_NULL NULL +#else +#define UTEST_CAST(type, x) ((type)(x)) +#define UTEST_PTR_CAST(type, x) ((type)(x)) +#define UTEST_EXTERN extern +#define UTEST_NULL 0 +#endif + +#ifdef _MSC_VER +/* + io.h contains definitions for some structures with natural padding. This is + uninteresting, but for some reason MSVC's behaviour is to warn about + including this system header. That *is* interesting +*/ +#pragma warning(disable : 4820) +#pragma warning(push, 1) +#include <io.h> +#pragma warning(pop) +#define UTEST_COLOUR_OUTPUT() (_isatty(_fileno(stdout))) +#else +#if defined(__EMSCRIPTEN__) +#include <emscripten/html5.h> +#define UTEST_COLOUR_OUTPUT() false +#else +#include <unistd.h> +#define UTEST_COLOUR_OUTPUT() (isatty(STDOUT_FILENO)) +#endif +#endif + +static UTEST_INLINE void *utest_realloc(void *const pointer, size_t new_size) { + void *const new_pointer = realloc(pointer, new_size); + + if (UTEST_NULL == new_pointer) { + free(new_pointer); + } + + return new_pointer; +} + +static UTEST_INLINE utest_int64_t utest_ns(void) { +#if defined(_MSC_VER) || defined(__MINGW64__) || defined(__MINGW32__) + utest_large_integer counter; + utest_large_integer frequency; + QueryPerformanceCounter(&counter); + QueryPerformanceFrequency(&frequency); + return UTEST_CAST(utest_int64_t, + (counter.QuadPart * 1000000000) / frequency.QuadPart); +#elif defined(__linux__) && defined(__STRICT_ANSI__) + return UTEST_CAST(utest_int64_t, clock()) * 1000000000 / CLOCKS_PER_SEC; +#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \ + defined(__NetBSD__) || defined(__DragonFly__) || defined(__sun__) || \ + defined(__HAIKU__) + struct timespec ts; +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ + !defined(__HAIKU__) + timespec_get(&ts, TIME_UTC); +#else + const clockid_t cid = CLOCK_REALTIME; +#if defined(UTEST_USE_CLOCKGETTIME) + clock_gettime(cid, &ts); +#else + syscall(SYS_clock_gettime, cid, &ts); +#endif +#endif + return UTEST_CAST(utest_int64_t, ts.tv_sec) * 1000 * 1000 * 1000 + ts.tv_nsec; +#elif __APPLE__ + return UTEST_CAST(utest_int64_t, clock_gettime_nsec_np(CLOCK_UPTIME_RAW)); +#elif __EMSCRIPTEN__ + return emscripten_performance_now() * 1000000.0; +#else +#error Unsupported platform! +#endif +} + +typedef void (*utest_testcase_t)(int *, size_t); + +struct utest_test_state_s { + utest_testcase_t func; + size_t index; + char *name; +}; + +struct utest_state_s { + struct utest_test_state_s *tests; + size_t tests_length; + FILE *output; +}; + +/* extern to the global state utest needs to execute */ +UTEST_EXTERN struct utest_state_s utest_state; + +#if defined(_MSC_VER) +#define UTEST_WEAK __forceinline +#elif defined(__MINGW32__) || defined(__MINGW64__) +#define UTEST_WEAK static UTEST_ATTRIBUTE(used) +#elif defined(__clang__) || defined(__GNUC__) || defined(__TINYC__) +#define UTEST_WEAK UTEST_ATTRIBUTE(weak) +#else +#error Non clang, non gcc, non MSVC, non tcc compiler found! +#endif + +#if defined(_MSC_VER) +#define UTEST_UNUSED +#else +#define UTEST_UNUSED UTEST_ATTRIBUTE(unused) +#endif + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wvariadic-macros" +#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif +#define UTEST_PRINTF(...) \ + if (utest_state.output) { \ + fprintf(utest_state.output, __VA_ARGS__); \ + } \ + printf(__VA_ARGS__) +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wvariadic-macros" +#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif + +#ifdef _MSC_VER +#define UTEST_SNPRINTF(BUFFER, N, ...) _snprintf_s(BUFFER, N, N, __VA_ARGS__) +#else +#define UTEST_SNPRINTF(...) snprintf(__VA_ARGS__) +#endif + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#if defined(__cplusplus) +/* if we are using c++ we can use overloaded methods (its in the language) */ +#define UTEST_OVERLOADABLE +#elif defined(__clang__) +/* otherwise, if we are using clang with c - use the overloadable attribute */ +#define UTEST_OVERLOADABLE UTEST_ATTRIBUTE(overloadable) +#endif + +#if defined(__cplusplus) && (__cplusplus >= 201103L) + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif + +#include <type_traits> + +template <typename T, bool is_enum = std::is_enum<T>::value> +struct utest_type_deducer final { + static void _(const T t); +}; + +template <> struct utest_type_deducer<signed char, false> { + static void _(const signed char c) { + UTEST_PRINTF("%d", static_cast<int>(c)); + } +}; + +template <> struct utest_type_deducer<unsigned char, false> { + static void _(const unsigned char c) { + UTEST_PRINTF("%u", static_cast<unsigned int>(c)); + } +}; + +template <> struct utest_type_deducer<short, false> { + static void _(const short s) { UTEST_PRINTF("%d", static_cast<int>(s)); } +}; + +template <> struct utest_type_deducer<unsigned short, false> { + static void _(const unsigned short s) { + UTEST_PRINTF("%u", static_cast<int>(s)); + } +}; + +template <> struct utest_type_deducer<float, false> { + static void _(const float f) { UTEST_PRINTF("%f", static_cast<double>(f)); } +}; + +template <> struct utest_type_deducer<double, false> { + static void _(const double d) { UTEST_PRINTF("%f", d); } +}; + +template <> struct utest_type_deducer<long double, false> { + static void _(const long double d) { +#if defined(__MINGW32__) || defined(__MINGW64__) + /* MINGW is weird - doesn't like LF at all?! */ + UTEST_PRINTF("%f", (double)d); +#else + UTEST_PRINTF("%Lf", d); +#endif + } +}; + +template <> struct utest_type_deducer<int, false> { + static void _(const int i) { UTEST_PRINTF("%d", i); } +}; + +template <> struct utest_type_deducer<unsigned int, false> { + static void _(const unsigned int i) { UTEST_PRINTF("%u", i); } +}; + +template <> struct utest_type_deducer<long, false> { + static void _(const long i) { UTEST_PRINTF("%ld", i); } +}; + +template <> struct utest_type_deducer<unsigned long, false> { + static void _(const unsigned long i) { UTEST_PRINTF("%lu", i); } +}; + +template <> struct utest_type_deducer<long long, false> { + static void _(const long long i) { UTEST_PRINTF("%lld", i); } +}; + +template <> struct utest_type_deducer<unsigned long long, false> { + static void _(const unsigned long long i) { UTEST_PRINTF("%llu", i); } +}; + +template <typename T> struct utest_type_deducer<const T *, false> { + static void _(const T *t) { + UTEST_PRINTF("%p", static_cast<void *>(const_cast<T *>(t))); + } +}; + +template <typename T> struct utest_type_deducer<T *, false> { + static void _(T *t) { UTEST_PRINTF("%p", static_cast<void *>(t)); } +}; + +template <typename T> struct utest_type_deducer<T, true> { + static void _(const T t) { + UTEST_PRINTF("%llu", static_cast<unsigned long long>(t)); + } +}; + +template <typename T> +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(const T t) { + utest_type_deducer<T>::_(t); +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#elif defined(UTEST_OVERLOADABLE) + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(signed char c); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(signed char c) { + UTEST_PRINTF("%d", UTEST_CAST(int, c)); +} + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(unsigned char c); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(unsigned char c) { + UTEST_PRINTF("%u", UTEST_CAST(unsigned int, c)); +} + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(float f); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(float f) { + UTEST_PRINTF("%f", UTEST_CAST(double, f)); +} + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(double d); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(double d) { + UTEST_PRINTF("%f", d); +} + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long double d); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long double d) { +#if defined(__MINGW32__) || defined(__MINGW64__) + /* MINGW is weird - doesn't like LF at all?! */ + UTEST_PRINTF("%f", (double)d); +#else + UTEST_PRINTF("%Lf", d); +#endif +} + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(int i); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(int i) { + UTEST_PRINTF("%d", i); +} + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(unsigned int i); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(unsigned int i) { + UTEST_PRINTF("%u", i); +} + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long int i); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long int i) { + UTEST_PRINTF("%ld", i); +} + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long unsigned int i); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long unsigned int i) { + UTEST_PRINTF("%lu", i); +} + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(const void *p); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(const void *p) { + UTEST_PRINTF("%p", p); +} + +/* + long long is a c++11 extension +*/ +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) || \ + defined(__cplusplus) && (__cplusplus >= 201103L) || \ + (defined(__MINGW32__) || defined(__MINGW64__)) + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long long int i); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long long int i) { + UTEST_PRINTF("%lld", i); +} + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long long unsigned int i); +UTEST_WEAK UTEST_OVERLOADABLE void +utest_type_printer(long long unsigned int i) { + UTEST_PRINTF("%llu", i); +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#endif +#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ + !(defined(__MINGW32__) || defined(__MINGW64__)) || \ + defined(__TINYC__) +#define utest_type_printer(val) \ + UTEST_PRINTF(_Generic((val), signed char \ + : "%d", unsigned char \ + : "%u", short \ + : "%d", unsigned short \ + : "%u", int \ + : "%d", long \ + : "%ld", long long \ + : "%lld", unsigned \ + : "%u", unsigned long \ + : "%lu", unsigned long long \ + : "%llu", float \ + : "%f", double \ + : "%f", long double \ + : "%Lf", default \ + : _Generic((val - val), ptrdiff_t \ + : "%p", default \ + : "undef")), \ + (val)) +#else +/* + we don't have the ability to print the values we got, so we create a macro + to tell our users we can't do anything fancy +*/ +#define utest_type_printer(...) UTEST_PRINTF("undef") +#endif + +#if defined(_MSC_VER) +#define UTEST_SURPRESS_WARNING_BEGIN \ + __pragma(warning(push)) __pragma(warning(disable : 4127)) \ + __pragma(warning(disable : 4571)) __pragma(warning(disable : 4130)) +#define UTEST_SURPRESS_WARNING_END __pragma(warning(pop)) +#else +#define UTEST_SURPRESS_WARNING_BEGIN +#define UTEST_SURPRESS_WARNING_END +#endif + +#if defined(__cplusplus) && (__cplusplus >= 201103L) +#define UTEST_AUTO(x) auto +#elif !defined(__cplusplus) + +#if defined(__clang__) +/* clang-format off */ +/* had to disable clang-format here because it malforms the pragmas */ +#define UTEST_AUTO(x) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wgnu-auto-type\"") __auto_type \ + _Pragma("clang diagnostic pop") +/* clang-format on */ +#else +#define UTEST_AUTO(x) __typeof__(x + 0) +#endif + +#else +#define UTEST_AUTO(x) typeof(x + 0) +#endif + +#if defined(__clang__) +#define UTEST_STRNCMP(x, y, size) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wdisabled-macro-expansion\"") \ + strncmp(x, y, size) _Pragma("clang diagnostic pop") +#else +#define UTEST_STRNCMP(x, y, size) strncmp(x, y, size) +#endif + +#if defined(_MSC_VER) +#define UTEST_STRNCPY(x, y, size) strcpy_s(x, size, y) +#elif !defined(__clang__) && defined(__GNUC__) +static UTEST_INLINE char * +utest_strncpy_gcc(char *const dst, const char *const src, const size_t size) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-overflow" + return strncpy(dst, src, size); +#pragma GCC diagnostic pop +} + +#define UTEST_STRNCPY(x, y, size) utest_strncpy_gcc(x, y, size) +#else +#define UTEST_STRNCPY(x, y, size) strncpy(x, y, size) +#endif + +#define UTEST_SKIP(msg) \ + do { \ + UTEST_PRINTF(" Skipped : '%s'\n", (msg)); \ + *utest_result = UTEST_TEST_SKIPPED; \ + return; \ + } while (0) + +#if defined(__clang__) +#define UTEST_COND(x, y, cond, msg, is_assert) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wlanguage-extension-token\"") \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat-pedantic\"") \ + _Pragma("clang diagnostic ignored \"-Wfloat-equal\"") \ + UTEST_AUTO(x) xEval = (x); \ + UTEST_AUTO(y) yEval = (y); \ + if (!((xEval)cond(yEval))) { \ + _Pragma("clang diagnostic pop") \ + UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : ("); \ + UTEST_PRINTF(#x ") " #cond " (" #y); \ + UTEST_PRINTF(")\n"); \ + UTEST_PRINTF(" Actual : "); \ + utest_type_printer(xEval); \ + UTEST_PRINTF(" vs "); \ + utest_type_printer(yEval); \ + UTEST_PRINTF("\n"); \ + if (strlen(msg) > 0) { \ + UTEST_PRINTF(" Message : %s\n", msg); \ + } \ + *utest_result = UTEST_TEST_FAILURE; \ + if (is_assert) { \ + return; \ + } \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END +#elif defined(__GNUC__) || defined(__TINYC__) +#define UTEST_COND(x, y, cond, msg, is_assert) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + UTEST_AUTO(x) xEval = (x); \ + UTEST_AUTO(y) yEval = (y); \ + if (!((xEval)cond(yEval))) { \ + UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : ("); \ + UTEST_PRINTF(#x ") " #cond " (" #y); \ + UTEST_PRINTF(")\n"); \ + UTEST_PRINTF(" Actual : "); \ + utest_type_printer(xEval); \ + UTEST_PRINTF(" vs "); \ + utest_type_printer(yEval); \ + UTEST_PRINTF("\n"); \ + if (strlen(msg) > 0) { \ + UTEST_PRINTF(" Message : %s\n", msg); \ + } \ + *utest_result = UTEST_TEST_FAILURE; \ + if (is_assert) { \ + return; \ + } \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END +#else +#define UTEST_COND(x, y, cond, msg, is_assert) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + if (!((x)cond(y))) { \ + UTEST_PRINTF("%s:%i: Failure (Expected " #cond " Actual)", __FILE__, \ + __LINE__); \ + if (strlen(msg) > 0) { \ + UTEST_PRINTF(" Message : %s", msg); \ + } \ + UTEST_PRINTF("\n"); \ + *utest_result = UTEST_TEST_FAILURE; \ + if (is_assert) { \ + return; \ + } \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END +#endif + +#define EXPECT_EQ(x, y) UTEST_COND(x, y, ==, "", 0) +#define EXPECT_EQ_MSG(x, y, msg) UTEST_COND(x, y, ==, msg, 0) +#define ASSERT_EQ(x, y) UTEST_COND(x, y, ==, "", 1) +#define ASSERT_EQ_MSG(x, y, msg) UTEST_COND(x, y, ==, msg, 1) + +#define EXPECT_NE(x, y) UTEST_COND(x, y, !=, "", 0) +#define EXPECT_NE_MSG(x, y, msg) UTEST_COND(x, y, !=, msg, 0) +#define ASSERT_NE(x, y) UTEST_COND(x, y, !=, "", 1) +#define ASSERT_NE_MSG(x, y, msg) UTEST_COND(x, y, !=, msg, 1) + +#define EXPECT_LT(x, y) UTEST_COND(x, y, <, "", 0) +#define EXPECT_LT_MSG(x, y, msg) UTEST_COND(x, y, <, msg, 0) +#define ASSERT_LT(x, y) UTEST_COND(x, y, <, "", 1) +#define ASSERT_LT_MSG(x, y, msg) UTEST_COND(x, y, <, msg, 1) + +#define EXPECT_LE(x, y) UTEST_COND(x, y, <=, "", 0) +#define EXPECT_LE_MSG(x, y, msg) UTEST_COND(x, y, <=, msg, 0) +#define ASSERT_LE(x, y) UTEST_COND(x, y, <=, "", 1) +#define ASSERT_LE_MSG(x, y, msg) UTEST_COND(x, y, <=, msg, 1) + +#define EXPECT_GT(x, y) UTEST_COND(x, y, >, "", 0) +#define EXPECT_GT_MSG(x, y, msg) UTEST_COND(x, y, >, msg, 0) +#define ASSERT_GT(x, y) UTEST_COND(x, y, >, "", 1) +#define ASSERT_GT_MSG(x, y, msg) UTEST_COND(x, y, >, msg, 1) + +#define EXPECT_GE(x, y) UTEST_COND(x, y, >=, "", 0) +#define EXPECT_GE_MSG(x, y, msg) UTEST_COND(x, y, >=, msg, 0) +#define ASSERT_GE(x, y) UTEST_COND(x, y, >=, "", 1) +#define ASSERT_GE_MSG(x, y, msg) UTEST_COND(x, y, >=, msg, 1) + +#define UTEST_TRUE(x, msg, is_assert) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + const int xEval = !!(x); \ + if (!(xEval)) { \ + UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : true\n"); \ + UTEST_PRINTF(" Actual : %s\n", (xEval) ? "true" : "false"); \ + if (strlen(msg) > 0) { \ + UTEST_PRINTF(" Message : %s\n", msg); \ + } \ + *utest_result = UTEST_TEST_FAILURE; \ + if (is_assert) { \ + return; \ + } \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END + +#define EXPECT_TRUE(x) UTEST_TRUE(x, "", 0) +#define EXPECT_TRUE_MSG(x, msg) UTEST_TRUE(x, msg, 0) +#define ASSERT_TRUE(x) UTEST_TRUE(x, "", 1) +#define ASSERT_TRUE_MSG(x, msg) UTEST_TRUE(x, msg, 1) + +#define UTEST_FALSE(x, msg, is_assert) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + const int xEval = !!(x); \ + if (xEval) { \ + UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : false\n"); \ + UTEST_PRINTF(" Actual : %s\n", (xEval) ? "true" : "false"); \ + if (strlen(msg) > 0) { \ + UTEST_PRINTF(" Message : %s\n", msg); \ + } \ + *utest_result = UTEST_TEST_FAILURE; \ + if (is_assert) { \ + return; \ + } \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END + +#define EXPECT_FALSE(x) UTEST_FALSE(x, "", 0) +#define EXPECT_FALSE_MSG(x, msg) UTEST_FALSE(x, msg, 0) +#define ASSERT_FALSE(x) UTEST_FALSE(x, "", 1) +#define ASSERT_FALSE_MSG(x, msg) UTEST_FALSE(x, msg, 1) + +#define UTEST_STREQ(x, y, msg, is_assert) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + const char *xEval = (x); \ + const char *yEval = (y); \ + if (UTEST_NULL == xEval || UTEST_NULL == yEval || \ + 0 != strcmp(xEval, yEval)) { \ + UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : \"%s\"\n", xEval); \ + UTEST_PRINTF(" Actual : \"%s\"\n", yEval); \ + if (strlen(msg) > 0) { \ + UTEST_PRINTF(" Message : %s\n", msg); \ + } \ + *utest_result = UTEST_TEST_FAILURE; \ + if (is_assert) { \ + return; \ + } \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END + +#define EXPECT_STREQ(x, y) UTEST_STREQ(x, y, "", 0) +#define EXPECT_STREQ_MSG(x, y, msg) UTEST_STREQ(x, y, msg, 0) +#define ASSERT_STREQ(x, y) UTEST_STREQ(x, y, "", 1) +#define ASSERT_STREQ_MSG(x, y, msg) UTEST_STREQ(x, y, msg, 1) + +#define UTEST_STRNE(x, y, msg, is_assert) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + const char *xEval = (x); \ + const char *yEval = (y); \ + if (UTEST_NULL == xEval || UTEST_NULL == yEval || \ + 0 == strcmp(xEval, yEval)) { \ + UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : \"%s\"\n", xEval); \ + UTEST_PRINTF(" Actual : \"%s\"\n", yEval); \ + if (strlen(msg) > 0) { \ + UTEST_PRINTF(" Message : %s\n", msg); \ + } \ + *utest_result = UTEST_TEST_FAILURE; \ + if (is_assert) { \ + return; \ + } \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END + +#define EXPECT_STRNE(x, y) UTEST_STRNE(x, y, "", 0) +#define EXPECT_STRNE_MSG(x, y, msg) UTEST_STRNE(x, y, msg, 0) +#define ASSERT_STRNE(x, y) UTEST_STRNE(x, y, "", 1) +#define ASSERT_STRNE_MSG(x, y, msg) UTEST_STRNE(x, y, msg, 1) + +#define UTEST_STRNEQ(x, y, n, msg, is_assert) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + const char *xEval = (x); \ + const char *yEval = (y); \ + const size_t nEval = UTEST_CAST(size_t, n); \ + if (UTEST_NULL == xEval || UTEST_NULL == yEval || \ + 0 != UTEST_STRNCMP(xEval, yEval, nEval)) { \ + UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : \"%.*s\"\n", UTEST_CAST(int, nEval), xEval); \ + UTEST_PRINTF(" Actual : \"%.*s\"\n", UTEST_CAST(int, nEval), yEval); \ + if (strlen(msg) > 0) { \ + UTEST_PRINTF(" Message : %s\n", msg); \ + } \ + *utest_result = UTEST_TEST_FAILURE; \ + if (is_assert) { \ + return; \ + } \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END + +#define EXPECT_STRNEQ(x, y, n) UTEST_STRNEQ(x, y, n, "", 0) +#define EXPECT_STRNEQ_MSG(x, y, n, msg) UTEST_STRNEQ(x, y, n, msg, 0) +#define ASSERT_STRNEQ(x, y, n) UTEST_STRNEQ(x, y, n, "", 1) +#define ASSERT_STRNEQ_MSG(x, y, n, msg) UTEST_STRNEQ(x, y, n, msg, 1) + +#define UTEST_STRNNE(x, y, n, msg, is_assert) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + const char *xEval = (x); \ + const char *yEval = (y); \ + const size_t nEval = UTEST_CAST(size_t, n); \ + if (UTEST_NULL == xEval || UTEST_NULL == yEval || \ + 0 == UTEST_STRNCMP(xEval, yEval, nEval)) { \ + UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : \"%.*s\"\n", UTEST_CAST(int, nEval), xEval); \ + UTEST_PRINTF(" Actual : \"%.*s\"\n", UTEST_CAST(int, nEval), yEval); \ + if (strlen(msg) > 0) { \ + UTEST_PRINTF(" Message : %s\n", msg); \ + } \ + *utest_result = UTEST_TEST_FAILURE; \ + if (is_assert) { \ + return; \ + } \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END + +#define EXPECT_STRNNE(x, y, n) UTEST_STRNNE(x, y, n, "", 0) +#define EXPECT_STRNNE_MSG(x, y, n, msg) UTEST_STRNNE(x, y, n, msg, 0) +#define ASSERT_STRNNE(x, y, n) UTEST_STRNNE(x, y, n, "", 1) +#define ASSERT_STRNNE_MSG(x, y, n, msg) UTEST_STRNNE(x, y, n, msg, 1) + +#define UTEST_NEAR(x, y, epsilon, msg, is_assert) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + const double diff = \ + utest_fabs(UTEST_CAST(double, x) - UTEST_CAST(double, y)); \ + if (diff > UTEST_CAST(double, epsilon) || utest_isnan(diff)) { \ + UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : %f\n", UTEST_CAST(double, x)); \ + UTEST_PRINTF(" Actual : %f\n", UTEST_CAST(double, y)); \ + if (strlen(msg) > 0) { \ + UTEST_PRINTF(" Message : %s\n", msg); \ + } \ + *utest_result = UTEST_TEST_FAILURE; \ + if (is_assert) { \ + return; \ + } \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END + +#define EXPECT_NEAR(x, y, epsilon) UTEST_NEAR(x, y, epsilon, "", 0) +#define EXPECT_NEAR_MSG(x, y, epsilon, msg) UTEST_NEAR(x, y, epsilon, msg, 0) +#define ASSERT_NEAR(x, y, epsilon) UTEST_NEAR(x, y, epsilon, "", 1) +#define ASSERT_NEAR_MSG(x, y, epsilon, msg) UTEST_NEAR(x, y, epsilon, msg, 1) + +#if defined(UTEST_HAS_EXCEPTIONS) +#define UTEST_EXCEPTION(x, exception_type, msg, is_assert) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + int exception_caught = 0; \ + try { \ + x; \ + } catch (const exception_type &) { \ + exception_caught = 1; \ + } catch (...) { \ + exception_caught = 2; \ + } \ + if (1 != exception_caught) { \ + UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : %s exception\n", #exception_type); \ + UTEST_PRINTF(" Actual : %s\n", (2 == exception_caught) \ + ? "Unexpected exception" \ + : "No exception"); \ + if (strlen(msg) > 0) { \ + UTEST_PRINTF(" Message : %s\n", msg); \ + } \ + *utest_result = UTEST_TEST_FAILURE; \ + if (is_assert) { \ + return; \ + } \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END + +#define EXPECT_EXCEPTION(x, exception_type) \ + UTEST_EXCEPTION(x, exception_type, "", 0) +#define EXPECT_EXCEPTION_MSG(x, exception_type, msg) \ + UTEST_EXCEPTION(x, exception_type, msg, 0) +#define ASSERT_EXCEPTION(x, exception_type) \ + UTEST_EXCEPTION(x, exception_type, "", 1) +#define ASSERT_EXCEPTION_MSG(x, exception_type, msg) \ + UTEST_EXCEPTION(x, exception_type, msg, 1) + +#define UTEST_EXCEPTION_WITH_MESSAGE(x, exception_type, exception_message, \ + msg, is_assert) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + int exception_caught = 0; \ + char *message_caught = UTEST_NULL; \ + try { \ + x; \ + } catch (const exception_type &e) { \ + const char *const what = e.what(); \ + exception_caught = 1; \ + if (0 != \ + UTEST_STRNCMP(what, exception_message, strlen(exception_message))) { \ + const size_t message_size = strlen(what) + 1; \ + message_caught = UTEST_PTR_CAST(char *, malloc(message_size)); \ + UTEST_STRNCPY(message_caught, what, message_size); \ + } \ + } catch (...) { \ + exception_caught = 2; \ + } \ + if (1 != exception_caught) { \ + UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : %s exception\n", #exception_type); \ + UTEST_PRINTF(" Actual : %s\n", (2 == exception_caught) \ + ? "Unexpected exception" \ + : "No exception"); \ + if (strlen(msg) > 0) { \ + UTEST_PRINTF(" Message : %s\n", msg); \ + } \ + *utest_result = UTEST_TEST_FAILURE; \ + if (is_assert) { \ + return; \ + } \ + } else if (UTEST_NULL != message_caught) { \ + UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : %s exception with message %s\n", \ + #exception_type, exception_message); \ + UTEST_PRINTF(" Actual message : %s\n", message_caught); \ + if (strlen(msg) > 0) { \ + UTEST_PRINTF(" Message : %s\n", msg); \ + } \ + *utest_result = UTEST_TEST_FAILURE; \ + free(message_caught); \ + if (is_assert) { \ + return; \ + } \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END + +#define EXPECT_EXCEPTION_WITH_MESSAGE(x, exception_type, exception_message) \ + UTEST_EXCEPTION_WITH_MESSAGE(x, exception_type, exception_message, "", 0) +#define EXPECT_EXCEPTION_WITH_MESSAGE_MSG(x, exception_type, \ + exception_message, msg) \ + UTEST_EXCEPTION_WITH_MESSAGE(x, exception_type, exception_message, msg, 0) +#define ASSERT_EXCEPTION_WITH_MESSAGE(x, exception_type, exception_message) \ + UTEST_EXCEPTION_WITH_MESSAGE(x, exception_type, exception_message, "", 1) +#define ASSERT_EXCEPTION_WITH_MESSAGE_MSG(x, exception_type, \ + exception_message, msg) \ + UTEST_EXCEPTION_WITH_MESSAGE(x, exception_type, exception_message, msg, 1) +#endif + +#if defined(__clang__) +#if __has_warning("-Wunsafe-buffer-usage") +#define UTEST_SURPRESS_WARNINGS_BEGIN \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wunsafe-buffer-usage\"") +#define UTEST_SURPRESS_WARNINGS_END _Pragma("clang diagnostic pop") +#else +#define UTEST_SURPRESS_WARNINGS_BEGIN +#define UTEST_SURPRESS_WARNINGS_END +#endif +#elif defined(__GNUC__) && __GNUC__ >= 8 && defined(__cplusplus) +#define UTEST_SURPRESS_WARNINGS_BEGIN \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wclass-memaccess\"") +#define UTEST_SURPRESS_WARNINGS_END _Pragma("GCC diagnostic pop") +#else +#define UTEST_SURPRESS_WARNINGS_BEGIN +#define UTEST_SURPRESS_WARNINGS_END +#endif + +#define UTEST(SET, NAME) \ + UTEST_SURPRESS_WARNINGS_BEGIN \ + UTEST_EXTERN struct utest_state_s utest_state; \ + static void utest_run_##SET##_##NAME(int *utest_result); \ + static void utest_##SET##_##NAME(int *utest_result, size_t utest_index) { \ + (void)utest_index; \ + utest_run_##SET##_##NAME(utest_result); \ + } \ + UTEST_INITIALIZER(utest_register_##SET##_##NAME) { \ + const size_t index = utest_state.tests_length++; \ + const char *name_part = #SET "." #NAME; \ + const size_t name_size = strlen(name_part) + 1; \ + char *name = UTEST_PTR_CAST(char *, malloc(name_size)); \ + utest_state.tests = UTEST_PTR_CAST( \ + struct utest_test_state_s *, \ + utest_realloc(UTEST_PTR_CAST(void *, utest_state.tests), \ + sizeof(struct utest_test_state_s) * \ + utest_state.tests_length)); \ + if (utest_state.tests) { \ + utest_state.tests[index].func = &utest_##SET##_##NAME; \ + utest_state.tests[index].name = name; \ + utest_state.tests[index].index = 0; \ + UTEST_SNPRINTF(name, name_size, "%s", name_part); \ + } else if (name) { \ + free(name); \ + } \ + } \ + UTEST_SURPRESS_WARNINGS_END \ + void utest_run_##SET##_##NAME(int *utest_result) + +#define UTEST_F_SETUP(FIXTURE) \ + static void utest_f_setup_##FIXTURE(int *utest_result, \ + struct FIXTURE *utest_fixture) + +#define UTEST_F_TEARDOWN(FIXTURE) \ + static void utest_f_teardown_##FIXTURE(int *utest_result, \ + struct FIXTURE *utest_fixture) + +#define UTEST_F(FIXTURE, NAME) \ + UTEST_SURPRESS_WARNINGS_BEGIN \ + UTEST_EXTERN struct utest_state_s utest_state; \ + static void utest_f_setup_##FIXTURE(int *, struct FIXTURE *); \ + static void utest_f_teardown_##FIXTURE(int *, struct FIXTURE *); \ + static void utest_run_##FIXTURE##_##NAME(int *, struct FIXTURE *); \ + static void utest_f_##FIXTURE##_##NAME(int *utest_result, \ + size_t utest_index) { \ + struct FIXTURE fixture; \ + (void)utest_index; \ + memset(&fixture, 0, sizeof(fixture)); \ + utest_f_setup_##FIXTURE(utest_result, &fixture); \ + if (UTEST_TEST_PASSED != *utest_result) { \ + return; \ + } \ + utest_run_##FIXTURE##_##NAME(utest_result, &fixture); \ + utest_f_teardown_##FIXTURE(utest_result, &fixture); \ + } \ + UTEST_INITIALIZER(utest_register_##FIXTURE##_##NAME) { \ + const size_t index = utest_state.tests_length++; \ + const char *name_part = #FIXTURE "." #NAME; \ + const size_t name_size = strlen(name_part) + 1; \ + char *name = UTEST_PTR_CAST(char *, malloc(name_size)); \ + utest_state.tests = UTEST_PTR_CAST( \ + struct utest_test_state_s *, \ + utest_realloc(UTEST_PTR_CAST(void *, utest_state.tests), \ + sizeof(struct utest_test_state_s) * \ + utest_state.tests_length)); \ + if (utest_state.tests) { \ + utest_state.tests[index].func = &utest_f_##FIXTURE##_##NAME; \ + utest_state.tests[index].name = name; \ + UTEST_SNPRINTF(name, name_size, "%s", name_part); \ + } else if (name) { \ + free(name); \ + } \ + } \ + UTEST_SURPRESS_WARNINGS_END \ + void utest_run_##FIXTURE##_##NAME(int *utest_result, \ + struct FIXTURE *utest_fixture) + +#define UTEST_I_SETUP(FIXTURE) \ + static void utest_i_setup_##FIXTURE( \ + int *utest_result, struct FIXTURE *utest_fixture, size_t utest_index) + +#define UTEST_I_TEARDOWN(FIXTURE) \ + static void utest_i_teardown_##FIXTURE( \ + int *utest_result, struct FIXTURE *utest_fixture, size_t utest_index) + +#define UTEST_I(FIXTURE, NAME, INDEX) \ + UTEST_SURPRESS_WARNINGS_BEGIN \ + UTEST_EXTERN struct utest_state_s utest_state; \ + static void utest_run_##FIXTURE##_##NAME##_##INDEX(int *, struct FIXTURE *); \ + static void utest_i_##FIXTURE##_##NAME##_##INDEX(int *utest_result, \ + size_t index) { \ + struct FIXTURE fixture; \ + memset(&fixture, 0, sizeof(fixture)); \ + utest_i_setup_##FIXTURE(utest_result, &fixture, index); \ + if (UTEST_TEST_PASSED != *utest_result) { \ + return; \ + } \ + utest_run_##FIXTURE##_##NAME##_##INDEX(utest_result, &fixture); \ + utest_i_teardown_##FIXTURE(utest_result, &fixture, index); \ + } \ + UTEST_INITIALIZER(utest_register_##FIXTURE##_##NAME##_##INDEX) { \ + size_t i; \ + utest_uint64_t iUp; \ + for (i = 0; i < (INDEX); i++) { \ + const size_t index = utest_state.tests_length++; \ + const char *name_part = #FIXTURE "." #NAME; \ + const size_t name_size = strlen(name_part) + 32; \ + char *name = UTEST_PTR_CAST(char *, malloc(name_size)); \ + utest_state.tests = UTEST_PTR_CAST( \ + struct utest_test_state_s *, \ + utest_realloc(UTEST_PTR_CAST(void *, utest_state.tests), \ + sizeof(struct utest_test_state_s) * \ + utest_state.tests_length)); \ + if (utest_state.tests) { \ + utest_state.tests[index].func = &utest_i_##FIXTURE##_##NAME##_##INDEX; \ + utest_state.tests[index].index = i; \ + utest_state.tests[index].name = name; \ + iUp = UTEST_CAST(utest_uint64_t, i); \ + UTEST_SNPRINTF(name, name_size, "%s/%" UTEST_PRIu64, name_part, iUp); \ + } else if (name) { \ + free(name); \ + } \ + } \ + } \ + UTEST_SURPRESS_WARNINGS_END \ + void utest_run_##FIXTURE##_##NAME##_##INDEX(int *utest_result, \ + struct FIXTURE *utest_fixture) + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif + +UTEST_WEAK +double utest_fabs(double d); +UTEST_WEAK +double utest_fabs(double d) { + union { + double d; + utest_uint64_t u; + } both; + both.d = d; + both.u &= 0x7fffffffffffffffu; + return both.d; +} + +UTEST_WEAK +int utest_isnan(double d); +UTEST_WEAK +int utest_isnan(double d) { + union { + double d; + utest_uint64_t u; + } both; + both.d = d; + both.u &= 0x7fffffffffffffffu; + return both.u > 0x7ff0000000000000u; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#if defined(__clang__) +#if __has_warning("-Wunsafe-buffer-usage") +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" +#endif +#endif + +UTEST_WEAK +int utest_should_filter_test(const char *filter, const char *testcase); +UTEST_WEAK int utest_should_filter_test(const char *filter, + const char *testcase) { + if (filter) { + const char *filter_cur = filter; + const char *testcase_cur = testcase; + const char *filter_wildcard = UTEST_NULL; + + while (('\0' != *filter_cur) && ('\0' != *testcase_cur)) { + if ('*' == *filter_cur) { + /* store the position of the wildcard */ + filter_wildcard = filter_cur; + + /* skip the wildcard character */ + filter_cur++; + + while (('\0' != *filter_cur) && ('\0' != *testcase_cur)) { + if ('*' == *filter_cur) { + /* + we found another wildcard (filter is something like *foo*) so we + exit the current loop, and return to the parent loop to handle + the wildcard case + */ + break; + } else if (*filter_cur != *testcase_cur) { + /* otherwise our filter didn't match, so reset it */ + filter_cur = filter_wildcard; + } + + /* move testcase along */ + testcase_cur++; + + /* move filter along */ + filter_cur++; + } + + if (('\0' == *filter_cur) && ('\0' == *testcase_cur)) { + return 0; + } + + /* if the testcase has been exhausted, we don't have a match! */ + if ('\0' == *testcase_cur) { + return 1; + } + } else { + if (*testcase_cur != *filter_cur) { + /* test case doesn't match filter */ + return 1; + } else { + /* move our filter and testcase forward */ + testcase_cur++; + filter_cur++; + } + } + } + + if (('\0' != *filter_cur) || + (('\0' != *testcase_cur) && + ((filter == filter_cur) || ('*' != filter_cur[-1])))) { + /* we have a mismatch! */ + return 1; + } + } + + return 0; +} + +static UTEST_INLINE FILE *utest_fopen(const char *filename, const char *mode) { +#ifdef _MSC_VER + FILE *file; + if (0 == fopen_s(&file, filename, mode)) { + return file; + } else { + return UTEST_NULL; + } +#else + return fopen(filename, mode); +#endif +} + +static UTEST_INLINE int utest_main(int argc, const char *const argv[]); +int utest_main(int argc, const char *const argv[]) { + utest_uint64_t failed = 0; + utest_uint64_t skipped = 0; + size_t index = 0; + size_t *failed_testcases = UTEST_NULL; + size_t failed_testcases_length = 0; + size_t *skipped_testcases = UTEST_NULL; + size_t skipped_testcases_length = 0; + const char *filter = UTEST_NULL; + utest_uint64_t ran_tests = 0; + int enable_mixed_units = 0; + int random_order = 0; + utest_uint32_t seed = 0; + + enum colours { RESET, GREEN, RED, YELLOW }; + + const int use_colours = UTEST_COLOUR_OUTPUT(); + const char *colours[] = {"\033[0m", "\033[32m", "\033[31m", "\033[33m"}; + + if (!use_colours) { + for (index = 0; index < sizeof colours / sizeof colours[0]; index++) { + colours[index] = ""; + } + } + /* loop through all arguments looking for our options */ + for (index = 1; index < UTEST_CAST(size_t, argc); index++) { + /* Informational switches */ + const char help_str[] = "--help"; + const char list_str[] = "--list-tests"; + /* Test config switches */ + const char filter_str[] = "--filter="; + const char output_str[] = "--output="; + const char enable_mixed_units_str[] = "--enable-mixed-units"; + const char random_order_str[] = "--random-order"; + const char random_order_with_seed_str[] = "--random-order="; + + if (0 == UTEST_STRNCMP(argv[index], help_str, strlen(help_str))) { + printf("utest.h - the single file unit testing solution for C/C++!\n" + "Command line Options:\n" + " --help Show this message and exit.\n" + " --filter=<filter> Filter the test cases to run (EG. " + "MyTest*.a would run MyTestCase.a but not MyTestCase.b).\n" + " --list-tests List testnames, one per line. Output " + "names can be passed to --filter.\n"); + printf(" --output=<output> Output an xunit XML file to the file " + "specified in <output>.\n" + " --enable-mixed-units Enable the per-test output to contain " + "mixed units (s/ms/us/ns).\n" + " --random-order[=<seed>] Randomize the order that the tests are " + "ran in. If the optional <seed> argument is not provided, then a " + "random starting seed is used.\n"); + goto cleanup; + } else if (0 == + UTEST_STRNCMP(argv[index], filter_str, strlen(filter_str))) { + /* user wants to filter what test cases run! */ + filter = argv[index] + strlen(filter_str); + } else if (0 == + UTEST_STRNCMP(argv[index], output_str, strlen(output_str))) { + utest_state.output = utest_fopen(argv[index] + strlen(output_str), "w+"); + } else if (0 == UTEST_STRNCMP(argv[index], list_str, strlen(list_str))) { + for (index = 0; index < utest_state.tests_length; index++) { + UTEST_PRINTF("%s\n", utest_state.tests[index].name); + } + /* when printing the test list, don't actually run the tests */ + return 0; + } else if (0 == UTEST_STRNCMP(argv[index], enable_mixed_units_str, + strlen(enable_mixed_units_str))) { + enable_mixed_units = 1; + } else if (0 == UTEST_STRNCMP(argv[index], random_order_with_seed_str, + strlen(random_order_with_seed_str))) { + seed = + UTEST_CAST(utest_uint32_t, + strtoul(argv[index] + strlen(random_order_with_seed_str), + UTEST_NULL, 10)); + random_order = 1; + } else if (0 == UTEST_STRNCMP(argv[index], random_order_str, + strlen(random_order_str))) { + const utest_int64_t ns = utest_ns(); + + // Some really poor pseudo-random using the current time. I do this + // because I really want to avoid using C's rand() because that'd mean our + // random would be affected by any srand() usage by the user (which I + // don't want). + seed = UTEST_CAST(utest_uint32_t, ns >> 32) * 31 + + UTEST_CAST(utest_uint32_t, ns & 0xffffffff); + random_order = 1; + } + } + + if (random_order) { + // Use Fisher-Yates with the Durstenfield's version to randomly re-order the + // tests. + for (index = utest_state.tests_length; index > 1; index--) { + // For the random order we'll use PCG. + const utest_uint32_t state = seed; + const utest_uint32_t word = + ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u; + const utest_uint32_t next = + ((word >> 22u) ^ word) % UTEST_CAST(utest_uint32_t, index); + + // Swap the randomly chosen element into the last location. + const struct utest_test_state_s copy = utest_state.tests[index - 1]; + utest_state.tests[index - 1] = utest_state.tests[next]; + utest_state.tests[next] = copy; + + // Move the seed onwards. + seed = seed * 747796405u + 2891336453u; + } + } + + for (index = 0; index < utest_state.tests_length; index++) { + if (utest_should_filter_test(filter, utest_state.tests[index].name)) { + continue; + } + + ran_tests++; + } + + printf("%s[==========]%s Running %" UTEST_PRIu64 " test cases.\n", + colours[GREEN], colours[RESET], UTEST_CAST(utest_uint64_t, ran_tests)); + + if (utest_state.output) { + fprintf(utest_state.output, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); + fprintf(utest_state.output, + "<testsuites tests=\"%" UTEST_PRIu64 "\" name=\"All\">\n", + UTEST_CAST(utest_uint64_t, ran_tests)); + fprintf(utest_state.output, + "<testsuite name=\"Tests\" tests=\"%" UTEST_PRIu64 "\">\n", + UTEST_CAST(utest_uint64_t, ran_tests)); + } + + for (index = 0; index < utest_state.tests_length; index++) { + int result = UTEST_TEST_PASSED; + utest_int64_t ns = 0; + + if (utest_should_filter_test(filter, utest_state.tests[index].name)) { + continue; + } + + printf("%s[ RUN ]%s %s\n", colours[GREEN], colours[RESET], + utest_state.tests[index].name); + + if (utest_state.output) { + fprintf(utest_state.output, "<testcase name=\"%s\">", + utest_state.tests[index].name); + } + + ns = utest_ns(); + errno = 0; +#if defined(UTEST_HAS_EXCEPTIONS) + UTEST_SURPRESS_WARNING_BEGIN + try { + utest_state.tests[index].func(&result, utest_state.tests[index].index); + } catch (const std::exception &err) { + printf(" Exception : %s\n", err.what()); + result = UTEST_TEST_FAILURE; + } catch (...) { + printf(" Exception : Unknown\n"); + result = UTEST_TEST_FAILURE; + } + UTEST_SURPRESS_WARNING_END +#else + utest_state.tests[index].func(&result, utest_state.tests[index].index); +#endif + ns = utest_ns() - ns; + + if (utest_state.output) { + fprintf(utest_state.output, "</testcase>\n"); + } + + // Record the failing test. + if (UTEST_TEST_FAILURE == result) { + const size_t failed_testcase_index = failed_testcases_length++; + failed_testcases = UTEST_PTR_CAST( + size_t *, utest_realloc(UTEST_PTR_CAST(void *, failed_testcases), + sizeof(size_t) * failed_testcases_length)); + if (UTEST_NULL != failed_testcases) { + failed_testcases[failed_testcase_index] = index; + } + failed++; + } else if (UTEST_TEST_SKIPPED == result) { + const size_t skipped_testcase_index = skipped_testcases_length++; + skipped_testcases = UTEST_PTR_CAST( + size_t *, utest_realloc(UTEST_PTR_CAST(void *, skipped_testcases), + sizeof(size_t) * skipped_testcases_length)); + if (UTEST_NULL != skipped_testcases) { + skipped_testcases[skipped_testcase_index] = index; + } + skipped++; + } + + { + const char *const units[] = {"ns", "us", "ms", "s", UTEST_NULL}; + unsigned int unit_index = 0; + utest_int64_t time = ns; + + if (enable_mixed_units) { + for (unit_index = 0; UTEST_NULL != units[unit_index]; unit_index++) { + if (10000 > time) { + break; + } + + time /= 1000; + } + } + + if (UTEST_TEST_FAILURE == result) { + printf("%s[ FAILED ]%s %s (%" UTEST_PRId64 "%s)\n", colours[RED], + colours[RESET], utest_state.tests[index].name, time, + units[unit_index]); + } else if (UTEST_TEST_SKIPPED == result) { + printf("%s[ SKIPPED ]%s %s (%" UTEST_PRId64 "%s)\n", colours[YELLOW], + colours[RESET], utest_state.tests[index].name, time, + units[unit_index]); + } else { + printf("%s[ OK ]%s %s (%" UTEST_PRId64 "%s)\n", colours[GREEN], + colours[RESET], utest_state.tests[index].name, time, + units[unit_index]); + } + } + } + + printf("%s[==========]%s %" UTEST_PRIu64 " test cases ran.\n", colours[GREEN], + colours[RESET], ran_tests); + printf("%s[ PASSED ]%s %" UTEST_PRIu64 " tests.\n", colours[GREEN], + colours[RESET], ran_tests - failed - skipped); + + if (0 != skipped) { + printf("%s[ SKIPPED ]%s %" UTEST_PRIu64 " tests, listed below:\n", + colours[YELLOW], colours[RESET], skipped); + for (index = 0; index < skipped_testcases_length; index++) { + printf("%s[ SKIPPED ]%s %s\n", colours[YELLOW], colours[RESET], + utest_state.tests[skipped_testcases[index]].name); + } + } + + if (0 != failed) { + printf("%s[ FAILED ]%s %" UTEST_PRIu64 " tests, listed below:\n", + colours[RED], colours[RESET], failed); + for (index = 0; index < failed_testcases_length; index++) { + printf("%s[ FAILED ]%s %s\n", colours[RED], colours[RESET], + utest_state.tests[failed_testcases[index]].name); + } + } + + if (utest_state.output) { + fprintf(utest_state.output, "</testsuite>\n</testsuites>\n"); + } + +cleanup: + for (index = 0; index < utest_state.tests_length; index++) { + free(UTEST_PTR_CAST(void *, utest_state.tests[index].name)); + } + + free(UTEST_PTR_CAST(void *, skipped_testcases)); + free(UTEST_PTR_CAST(void *, failed_testcases)); + free(UTEST_PTR_CAST(void *, utest_state.tests)); + + if (utest_state.output) { + fclose(utest_state.output); + } + + return UTEST_CAST(int, failed); +} + +#if defined(__clang__) +#if __has_warning("-Wunsafe-buffer-usage") +#pragma clang diagnostic pop +#endif +#endif + +/* + we need, in exactly one source file, define the global struct that will hold + the data we need to run utest. This macro allows the user to declare the + data without having to use the UTEST_MAIN macro, thus allowing them to write + their own main() function. +*/ +#define UTEST_STATE() struct utest_state_s utest_state = {0, 0, 0} + +/* + define a main() function to call into utest.h and start executing tests! A + user can optionally not use this macro, and instead define their own main() + function and manually call utest_main. The user must, in exactly one source + file, use the UTEST_STATE macro to declare a global struct variable that + utest requires. +*/ +#define UTEST_MAIN() \ + UTEST_STATE(); \ + int main(int argc, const char *const argv[]) { \ + return utest_main(argc, argv); \ + } + +#endif /* SHEREDOM_UTEST_H_INCLUDED */ diff --git a/Homework/math4610/test/vector.t.c b/Homework/math4610/test/vector.t.c new file mode 100644 index 0000000..4811113 --- /dev/null +++ b/Homework/math4610/test/vector.t.c @@ -0,0 +1,115 @@ +#include "lizfcm.test.h" +#include <math.h> + +UTEST(vector, copy_vector) { + Array_double *v = InitArray(double, {3, 1, -4}); + Array_double *w = copy_vector(v); + EXPECT_TRUE(vector_equal(v, w)); + + free_vector(v); + free_vector(w); +} + +UTEST(vector, add_element) { + Array_double *v = InitArray(double, {3, 1, -4}); + Array_double *w = add_element(v, -2); + Array_double *w_expect = InitArray(double, {3, 1, -4, -2}); + EXPECT_TRUE(vector_equal(w, w_expect)); + + free_vector(v); + free_vector(w); + free_vector(w_expect); +} + +UTEST(vector, slice_element) { + Array_double *v = InitArray(double, {3, 1, -4}); + Array_double *w = slice_element(v, 1); + Array_double *w_expect = InitArray(double, {3, -4}); + EXPECT_TRUE(vector_equal(w, w_expect)); + + free_vector(v); + free_vector(w); + free_vector(w_expect); +} + +UTEST(vector, free_vector) { + Array_double *v = InitArray(double, {3, 1, -4}); + uint64_t arr_addr = (uint64_t)v->data; + free_vector(v); + EXPECT_NE((uint64_t)v->data, arr_addr); +} + +UTEST(vector, sum_vector) { + Array_double *v = InitArray(double, {3, 1, -4}); + EXPECT_EQ(0.0, sum_v(v)); + free_vector(v); +} + +UTEST(vector, add_v) { + Array_double *a = InitArray(double, {1.0, 3.0, -4.0}); + Array_double *b = InitArray(double, {2.0, -1.0, 0}); + Array_double *expected_sum = InitArray(double, {3.0, 2.0, -4.0}); + Array_double *sum = add_v(a, b); + + EXPECT_TRUE(vector_equal(sum, expected_sum)); + + free_vector(a); + free_vector(b); + free_vector(expected_sum); + free_vector(sum); +} + +UTEST(vector, minus_v) { + Array_double *a = InitArray(double, {1.0, 3.0, -4.0}); + Array_double *b = InitArray(double, {2.0, -1.0, 0}); + Array_double *expected_sub = InitArray(double, {-1.0, 4.0, -4.0}); + Array_double *sub = minus_v(a, b); + + EXPECT_TRUE(vector_equal(sub, expected_sub)); + + free_vector(a); + free_vector(b); + free_vector(expected_sub); + free_vector(sub); +} + +UTEST(vector, scale_v) { + double factor = 3.0; + Array_double *a = InitArray(double, {1.0, 3.0, -4.0}); + Array_double *expected_scaled = InitArray(double, {3.0, 9.0, -12.0}); + Array_double *scaled = scale_v(a, factor); + + EXPECT_TRUE(vector_equal(scaled, expected_scaled)); + + free_vector(a); + free_vector(expected_scaled); + free_vector(scaled); +} + +UTEST(vector, l1_norm) { + Array_double *v = InitArray(double, {3, 1, -4}); + EXPECT_EQ(l1_norm(v), 8.0); + free_vector(v); +} + +UTEST(vector, l2_norm) { + Array_double *v = InitArray(double, {3, 1, -4}); + EXPECT_EQ(l2_norm(v), sqrt(3 * 3 + 1 * 1 + 4 * 4)); + free_vector(v); +} + +UTEST(vector, linf_norm) { + Array_double *v = InitArray(double, {3, 1, -4}); + EXPECT_EQ(linf_norm(v), c_max(c_max(3.0, 1.0), -4.0)); + free_vector(v); +} + +UTEST(vector, vector_distance) { + Array_double *v = InitArray(double, {3, 1, -4}); + Array_double *w = InitArray(double, {3, 1, -4}); + Array_double *minus = minus_v(v, w); + EXPECT_EQ(vector_distance(v, w, &l2_norm), l2_norm(minus)); + free_vector(v); + free_vector(w); + free_vector(minus); +} |
