openum 0.9.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
openum/BVP.py ADDED
@@ -0,0 +1,112 @@
1
+ import numpy as np
2
+ import matplotlib.pyplot as plt
3
+ from ODE import runge_kutta
4
+ from NonLinEq import linear_interpolation
5
+ from typing import Callable
6
+
7
+ def Matrix(h: float, n: int) -> np.ndarray:
8
+ """
9
+ Constructs a tridiagonal matrix for the finite difference method.
10
+
11
+ Args:
12
+ h (float): Step size.
13
+ n (int): Number of unknowns.
14
+
15
+ Returns:
16
+ np.ndarray: Tridiagonal matrix of size (n, n).
17
+ """
18
+ diag_main = -(2 + 2 * h**2) * np.ones(n)
19
+ diag_upper = np.ones(n - 1)
20
+ diag_lower = np.ones(n - 1)
21
+ A = np.diag(diag_main) + np.diag(diag_upper, 1) + np.diag(diag_lower, -1)
22
+ return A
23
+
24
+ def Vector(n: int, y0: float, yn: float) -> np.ndarray:
25
+ """
26
+ Constructs the right-hand side vector for the finite difference method.
27
+
28
+ Args:
29
+ n (int): Number of unknowns.
30
+ y0 (float): Boundary condition at x0.
31
+ yn (float): Boundary condition at xn.
32
+
33
+ Returns:
34
+ np.ndarray: Right-hand side vector.
35
+ """
36
+ b = np.zeros(n)
37
+ b[0] -= y0
38
+ b[-1] -= yn
39
+ return b
40
+
41
+ def Matrix_method(x0: float, xn: float, n: int, y0: float, yn: float) -> np.ndarray:
42
+ """
43
+ Solves a boundary value problem using the finite difference method.
44
+
45
+ Args:
46
+ x0 (float): Left boundary of the domain.
47
+ xn (float): Right boundary of the domain.
48
+ n (int): Number of interior grid points.
49
+ y0 (float): Boundary condition at x0.
50
+ yn (float): Boundary condition at xn.
51
+
52
+ Returns:
53
+ np.ndarray: Solution values at the grid points.
54
+ """
55
+ h = (xn - x0) / (n + 1)
56
+ list_x = np.linspace(x0, xn, n + 2)
57
+ list_y = np.zeros(n + 2)
58
+ list_y[0] = y0
59
+ list_y[-1] = yn
60
+
61
+ A = Matrix(h, n)
62
+ b = Vector(n, y0, yn)
63
+ y = np.linalg.solve(A, b)
64
+
65
+ for i in range(1, n + 1):
66
+ list_y[i] = y[i - 1]
67
+
68
+ # Plot solution
69
+ plt.figure(figsize=(8, 6))
70
+ plt.plot(list_x, list_y, color='b', label='Numerical Solution')
71
+ plt.xlabel('x')
72
+ plt.ylabel('y')
73
+ plt.grid(True)
74
+ plt.legend()
75
+ plt.show()
76
+
77
+ return y
78
+
79
+ def solve_by_shooting(ode: Callable, x_1: float, x_2: float, n: int, v_0: list, u_1: float, u_2: float, max_iter: int) -> tuple[float, np.ndarray, np.ndarray]:
80
+ """
81
+ Solves a boundary value problem using the shooting method.
82
+
83
+ Args:
84
+ ode (callable): Function defining the ODE system.
85
+ x_1 (float): Left boundary of the domain.
86
+ x_2 (float): Right boundary of the domain.
87
+ n (int): Number of interior grid points.
88
+ v_0 (list): Initial guesses for the derivative at x_1.
89
+ u_1 (float): Boundary condition at x_1.
90
+ u_2 (float): Boundary condition at x_2.
91
+ max_iter (int): Maximum number of iterations for root finding.
92
+
93
+ Returns:
94
+ tuple: (Corrected initial derivative, solution grid points, solution values)
95
+ """
96
+ def difference(v):
97
+ y_0 = [u_1, v]
98
+ _, y_values = runge_kutta(x_1, x_2, y_0, n, ode, False)
99
+ return y_values[-1, 0] - u_2
100
+
101
+ # Debug: Check function values at initial guesses
102
+ print(f"difference({v_0[0]}) = {difference(v_0[0])}")
103
+ print(f"difference({v_0[1]}) = {difference(v_0[1])}")
104
+
105
+ v_corr = linear_interpolation(difference, v_0[0], v_0[1], max_iter)
106
+
107
+ if v_corr is None:
108
+ raise RuntimeError("Root finding failed.")
109
+
110
+ y_0 = [u_1, v_corr]
111
+ x, y = runge_kutta(x_1, x_2, y_0, n, ode, False)
112
+ return v_corr, x, y
openum/Errorcalc.py ADDED
@@ -0,0 +1,64 @@
1
+ import numint, numdiff
2
+ from typing import Callable
3
+
4
+ class IntegrationandDiffMethodError:
5
+ def __init__(self, method, a: float, b: float, func:Callable):
6
+ """
7
+ Initialisiert die Integrationsmethode.
8
+
9
+ Args:
10
+ method (function): Die Integrationsmethode (z. B. Newton_cotes oder Trapezoidal)
11
+ a (float): Untere Grenze des Integrals
12
+ b (float): Obere Grenze des Integrals
13
+ func (function): Funktion, die integriert werden soll
14
+ """
15
+ self.method = method
16
+ self.n = 1000 # Standardanzahl der Unterintervalle
17
+ self.a = a
18
+ self.b = b
19
+ self.func = func
20
+ self.exact = None
21
+
22
+ def calculate_exact(self):
23
+ """
24
+ Führt die numerische Integration durch und speichert das Ergebnis.
25
+
26
+ Returns:
27
+ float: Das Ergebnis der numerischen Integration
28
+ """
29
+ self.exact = self.method(self.n, self.a, self.b, self.func)
30
+ return self.exact
31
+
32
+ def calculate_error(self, approx_value: float) -> float:
33
+ """
34
+ Berechnet den Fehler der numerischen Integration, falls der exakte Wert bekannt ist.
35
+
36
+ Args:
37
+ approx_value (float): Der approximierte Wert des Integrals
38
+
39
+ Returns:
40
+ float: Der Fehler der numerischen Integration
41
+ """
42
+ if self.exact is None:
43
+ self.calculate_exact()
44
+ return abs(approx_value - self.exact)
45
+
46
+
47
+ def error(method, a: float, b: float, func:Callable, approx_result: float) -> float:
48
+ """
49
+ Berechnet den Fehler der numerischen Integration.
50
+
51
+ Args:
52
+ method: Methode, die für die Berechnung des approximierten Werts verwendet wird
53
+ a (float): Untere Grenze des Integrals
54
+ b (float): Obere Grenze des Integrals
55
+ func: Funktion, die integriert werden muss
56
+ approx_result (float): Der approximierte Wert des Integrals
57
+
58
+ Returns:
59
+ error (float): Fehler der numerischen Integration
60
+ """
61
+ err_func = IntegrationandDiffMethodError(method, a, b, func)
62
+ exact_value = err_func.calculate_exact() # Calculate exact value
63
+ error_value = err_func.calculate_error(approx_result) # Calculate the error
64
+ return error_value
@@ -0,0 +1,68 @@
1
+ import numpy as np
2
+ from typing import Callable, Optional
3
+
4
+ def h(i, x):
5
+ return x[i + 1] - x[i] # Schrittweite zwischen den Punkten
6
+
7
+ def cubic_splines(n: int, x: np.ndarray, func: Callable) -> tuple[Optional[np.ndarray], Optional[np.ndarray], Optional[np.ndarray]]:
8
+ """
9
+ Constructs cubic splines for interpolation.
10
+
11
+ Args:
12
+ n (int): Number of intervals (len(x) - 1).
13
+ x (np.ndarray): Array of grid points.
14
+ func (Callable): Function to evaluate at grid points.
15
+
16
+ Returns:
17
+ tuple: A tuple containing (matrix, vec, solution), where 'matrix' is the
18
+ coefficient matrix, 'vec' is the right-hand side vector, and 'solution'
19
+ is the array of approximated spline second derivatives. Returns None
20
+ for elements if the calculation fails.
21
+ """
22
+ values = func(x)
23
+ if n < 2:
24
+ print("Nicht genügend Punkte für Splines")
25
+ return None, None, None
26
+
27
+ matrix = np.zeros((n - 1, n - 1), dtype=float)
28
+ vec = np.zeros(n - 1, dtype=float)
29
+
30
+ # Anfangsbedingungen
31
+ matrix[0, 0] = (h(0, x) + h(1, x)) * (h(0, x) + 2 * h(1, x)) / h(1, x)
32
+ if n > 2:
33
+ matrix[0, 1] = (h(1, x) ** 2 - h(0, x) ** 2) / h(1, x)
34
+
35
+ vec[0] = (values[2] - values[1]) / h(1, x) - (values[1] - values[0]) / h(0, x)
36
+
37
+ # Für den Rest der Matrix
38
+ for i in range(1, n - 2):
39
+ matrix[i, i - 1] = h(i, x)
40
+ matrix[i, i] = 2 * (h(i, x) + h(i + 1, x))
41
+ matrix[i, i + 1] = h(i + 1, x)
42
+ vec[i] = (values[i + 2] - values[i + 1]) / h(i + 1, x) - (values[i + 1] - values[i]) / h(i, x)
43
+
44
+ # Endbedingungen
45
+ if n > 2:
46
+ matrix[n - 2, n - 3] = (h(n - 3, x) ** 2 - h(n - 2, x) ** 2) / h(n - 3, x)
47
+ matrix[n - 2, n - 2] = (h(n - 2, x) + h(n - 1, x)) * (h(n - 2, x) + 2 * h(n - 1, x)) / h(n - 1, x)
48
+
49
+ vec[n - 2] = (values[n] - values[n - 1]) / h(n - 1, x) - (values[n - 1] - values[n - 2]) / h(n - 2, x)
50
+ vec *= 6
51
+
52
+ # Konditionierung der Matrix überprüfen
53
+ if np.linalg.cond(matrix) > 1e12:
54
+ print("Matrix ist schlecht konditioniert, Lösung könnte ungenau sein.")
55
+ return matrix, vec, None
56
+
57
+ try:
58
+ # Lösung des linearen Systems
59
+ solution = np.linalg.solve(matrix, vec)
60
+ S_0 = ((h(0, x) + h(1, x)) * solution[0] - h(0, x) * solution[1]) / h(1, x)
61
+ S_n = ((h(n - 2, x) + h(n - 1, x)) * solution[n - 2] - h(n - 1, x) * solution[n - 3]) / h(n - 2, x)
62
+
63
+ solution = np.append(solution, S_n)
64
+ solution = np.insert(solution, 0, S_0)
65
+ return matrix, vec, solution
66
+ except np.linalg.LinAlgError:
67
+ print("Keine Lösung vorhanden")
68
+ return matrix, vec, None
openum/MC.py ADDED
@@ -0,0 +1,61 @@
1
+ import numpy as np
2
+ from typing import Callable
3
+
4
+ def Monte_Carlo(n: int, a: float, b: float, func: Callable) -> tuple[float, float]:
5
+ """
6
+ Calculating the integral of a function via Monte Carlo integration.
7
+
8
+ Args:
9
+ n (int): Number of random samples
10
+ a (float): Lower bound of the integral
11
+ b (float): Upper bound of the integral
12
+ func : Function that needs to be integrated
13
+
14
+ Returns:
15
+ tuple: (result, error)
16
+ result (float): Approximated value of the integration
17
+ error (float): Estimated statistical error (Standard Error of the Mean)
18
+ """
19
+ n = int(n)
20
+
21
+ x_random = np.random.uniform(a, b, n)
22
+ f_values = func(x_random)
23
+
24
+ volume = b - a
25
+ result = volume * np.mean(f_values)
26
+
27
+ # Statistischer Fehler: Volumen * (Standardabweichung / Wurzel(n))
28
+ # ddof=1 für die unvezerrte Stichprobenvarianz
29
+ error = volume * (np.std(f_values, ddof=1) / np.sqrt(n))
30
+
31
+ return result, error
32
+
33
+
34
+ def Monte_Carlo_nD(n: int, bounds: list, func: Callable) -> tuple[float, float]:
35
+ """
36
+ Calculating the integral of a multi-dimensional function via Monte Carlo integration.
37
+
38
+ Args:
39
+ n (int): Number of random samples
40
+ bounds (list of tuples): [(lower, upper), ...] for each dimension
41
+ func : Function to integrate
42
+
43
+ Returns:
44
+ tuple: (result, error)
45
+ """
46
+ n = int(n)
47
+ dimensions = len(bounds)
48
+ volume = 1.0
49
+ random_points = np.zeros((n, dimensions))
50
+
51
+ for d in range(dimensions):
52
+ a, b = bounds[d]
53
+ random_points[:, d] = np.random.uniform(a, b, n)
54
+ volume *= (b - a)
55
+
56
+ f_values = np.array([func(point) for point in random_points])
57
+
58
+ result = volume * np.mean(f_values)
59
+ error = volume * (np.std(f_values, ddof=1) / np.sqrt(n))
60
+
61
+ return result, error
openum/NonLinEq.py ADDED
@@ -0,0 +1,258 @@
1
+ import numpy as np
2
+ from typing import Callable
3
+
4
+ def newtons_method(f: Callable, df: Callable, x0: float, max_iter: int, epsilon: float=1e-6) -> float:
5
+ """
6
+ Find the root of a function using Newton's method.
7
+
8
+ Args:
9
+ f (function): Function for which the root is to be found.
10
+ df (function): Derivative of the function.
11
+ x0 (float): Initial guess.
12
+ max_iter (int): Maximum number of iterations.
13
+ epsilon (float): Convergence criterion.
14
+
15
+ Returns:
16
+ x (float): Approximation of the root, or None if no convergence.
17
+ """
18
+ x = x0
19
+ for _ in range(max_iter):
20
+ fx = f(x)
21
+ dfx = df(x)
22
+
23
+ if np.abs(fx) < epsilon:
24
+ return x
25
+
26
+ if dfx == 0:
27
+ print("Derivative is zero. Cannot proceed.")
28
+ return None
29
+
30
+ x -= fx / dfx
31
+
32
+ print("Did not converge.")
33
+ return None
34
+
35
+
36
+ def linear_interpolation(f: Callable, x0: float, x1: float, max_iter: int, epsilon: float=1e-6) -> float:
37
+ """
38
+ Find the root of a function using linear interpolation.
39
+
40
+ Args:
41
+ f (function): Function for which the root is to be found.
42
+ x0 (float): Initial guess.
43
+ x1 (float): Second guess.
44
+ max_iter (int): Maximum number of iterations.
45
+ epsilon (float): Convergence criterion.
46
+
47
+ Returns:
48
+ x2 (float): Approximation of the root, or None if no convergence.
49
+ """
50
+ for _ in range(max_iter):
51
+ f0, f1 = f(x0), f(x1)
52
+
53
+ if f1 == f0: # Prevent division by zero
54
+ print("Division by zero encountered.")
55
+ return None
56
+
57
+ x2 = x1 - f1 * (x1 - x0) / (f1 - f0)
58
+
59
+ if np.abs(x2 - x1) < epsilon:
60
+ print(f"Root found: {f(x2)}")
61
+ return x2
62
+
63
+ x0, x1 = x1, x2
64
+
65
+ print("Did not converge.")
66
+ return None
67
+
68
+
69
+ def solve_fixed_point(f1: Callable, f2: Callable, x_init: float, y_init: float, max_iter: int, tol=1e-8) -> tuple[float,float]:
70
+ """
71
+ Solve a system of equations using fixed-point iteration.
72
+
73
+ Args:
74
+ f1 (function): Function for the first equation.
75
+ f2 (function): Function for the second equation.
76
+ x_init (float): Initial guess for x.
77
+ y_init (float): Initial guess for y.
78
+ max_iter (int): Maximum number of iterations.
79
+ tol (float): Convergence criterion.
80
+
81
+ Returns:
82
+ tuple: Approximations of the roots (x, y), or (None, None) if no convergence.
83
+ """
84
+ x, y = x_init, y_init
85
+
86
+ for i in range(max_iter):
87
+ x_new = f1(y)
88
+ y_new = f2(x)
89
+
90
+ if np.abs(x_new - x) < tol and np.abs(y_new - y) < tol:
91
+ print(f"Converged in {i + 1} iterations.")
92
+ return x_new, y_new
93
+
94
+ x, y = x_new, y_new
95
+
96
+ print("Did not converge.")
97
+ return None, None
98
+
99
+ def newton_halley(f: Callable, df: Callable, d2f: Callable, x0: float, max_iter: int, epsilon: float=1e-6) -> float:
100
+ """
101
+ Find the root of a function using Newton-Halley method.
102
+
103
+ Args:
104
+ f (function): Function for which the root is to be found.
105
+ df (function): First derivative of the function.
106
+ d2f (function): Second derivative of the function.
107
+ x0 (float): Initial guess.
108
+ max_iter (int): Maximum number of iterations.
109
+ epsilon (float): Convergence criterion.
110
+
111
+ Returns:
112
+ x (float): Approximation of the root, or None if no convergence.
113
+ """
114
+ x = x0
115
+ for _ in range(max_iter):
116
+ fx = f(x)
117
+ dfx = df(x)
118
+ d2fx = d2f(x)
119
+
120
+ if np.abs(fx) < epsilon:
121
+ return x
122
+
123
+ if dfx == 0:
124
+ print("Derivative is zero. Cannot proceed.")
125
+ return None
126
+
127
+ x -= (2*fx*dfx)/(2*dfx**2-d2fx*fx)
128
+
129
+ print("Did not converge.")
130
+ return None
131
+
132
+
133
+ def bisection_method(x1: float, x2: float, f: Callable, max_iter: int, eps: float=1e-6) -> float:
134
+ """
135
+ Solving nonlinear equations with bisection method.
136
+
137
+ Args:
138
+ x1 (float): Lower bound of the interval.
139
+ x2 (float): Upper bound of the interval.
140
+ f (function): Function for which the root is to be found.
141
+ max_iter (int): Maximum number of iterations.
142
+ eps (float): Convergence criterion.
143
+
144
+ Returns:
145
+ float: Approximation of the root, or None if no convergence.
146
+ """
147
+ x_new = (x1+x2)/2
148
+ for _ in range(max_iter):
149
+
150
+ if f(x_new)*f(x1) < 0:
151
+ x2 = x_new
152
+
153
+ elif f(x_new)*f(x2) < 0:
154
+ x1 = x_new
155
+
156
+ x_new = (x1+x2)/2
157
+
158
+ if np.abs(f(x_new)) < eps:
159
+ return x_new
160
+
161
+ print("Did not converge.")
162
+ return None
163
+
164
+ import numpy as np
165
+
166
+ def jacobi_method(A: np.ndarray, b: np.ndarray, init_val: list, max_iter: int, tol: float=1e-6) -> np.ndarray:
167
+ """
168
+ Solve a linear system using the iterative Jacobi method.
169
+
170
+ In contrast to Gauss-Seidel, the Jacobi method uses only the values
171
+ from the previous iteration step to calculate the new approximations.
172
+
173
+ Args:
174
+ A (numpy.ndarray): Coefficient matrix.
175
+ b (numpy.ndarray): Right-hand side vector.
176
+ init_val (list): Initial guess for the solution vector x.
177
+ max_iter (int): Maximum number of iterations.
178
+ tol (float): Convergence tolerance.
179
+
180
+ Returns:
181
+ numpy.ndarray: Solution vector if convergence is achieved.
182
+ """
183
+ rows, cols = A.shape
184
+ if rows != cols:
185
+ raise ValueError("Coefficient matrix A must be square.")
186
+
187
+ x_old = np.array(init_val, dtype=float)
188
+ x_new = np.zeros_like(x_old)
189
+
190
+ for iteration in range(max_iter):
191
+ for i in range(rows):
192
+ if A[i, i] == 0:
193
+ raise ValueError("Diagonal element is zero, cannot proceed with Jacobi method.")
194
+
195
+ # Sum over all j != i
196
+ s = 0.0
197
+ for j in range(cols):
198
+ if j != i:
199
+ s += A[i, j] * x_old[j]
200
+
201
+ x_new[i] = (b[i] - s) / A[i, i]
202
+
203
+ # Check for convergence (using infinity norm)
204
+ if np.linalg.norm(x_new - x_old, ord=np.inf) < tol:
205
+ return x_new
206
+
207
+ # Update x_old for the next iteration
208
+ x_old = x_new.copy()
209
+
210
+ raise RuntimeError("Jacobi method did not converge within the maximum number of iterations.")
211
+
212
+
213
+ def Gauss_elimination_pivoted(matrix: np.ndarray, vector: np.ndarray) -> np.ndarray:
214
+ """
215
+ Function for Gaussian elimination using partial pivoting to reduce numerical errors.
216
+
217
+ Swaps rows to ensure the largest absolute value in the current column
218
+ becomes the pivot element, minimizing floating-point inaccuracies.
219
+
220
+ Args:
221
+ matrix (numpy.ndarray): Coefficient matrix.
222
+ vector (numpy.ndarray): Right-hand side vector.
223
+
224
+ Returns:
225
+ x (numpy.ndarray): Solution vector.
226
+ """
227
+ U_Matrix = np.copy(matrix).astype(float)
228
+ U_vector = np.copy(vector).astype(float)
229
+ rows, columns = U_Matrix.shape
230
+ x = np.zeros(rows)
231
+
232
+ # Forward elimination with partial pivoting
233
+ for i in range(rows - 1):
234
+ # --- NEW: Partial Pivoting ---
235
+ # Find the row with the largest absolute value in the current column i
236
+ max_row_index = i + np.argmax(np.abs(U_Matrix[i:rows, i]))
237
+
238
+ if max_row_index != i:
239
+ # Swap the current row i with the row containing the maximum value
240
+ U_Matrix[[i, max_row_index]] = U_Matrix[[max_row_index, i]]
241
+ U_vector[[i, max_row_index]] = U_vector[[max_row_index, i]]
242
+ # -----------------------------
243
+
244
+ if np.isclose(U_Matrix[i, i], 0):
245
+ raise ValueError("Matrix is singular or nearly singular.")
246
+
247
+ for j in range(i + 1, rows):
248
+ factor = U_Matrix[j, i] / U_Matrix[i, i]
249
+ U_Matrix[j] -= factor * U_Matrix[i]
250
+ U_vector[j] -= factor * U_vector[i]
251
+
252
+ # Back substitution (same as before)
253
+ for i in range(rows - 1, -1, -1):
254
+ if np.isclose(U_Matrix[i, i], 0):
255
+ raise ValueError("Matrix is singular or nearly singular.")
256
+ x[i] = (U_vector[i] - np.dot(U_Matrix[i, i + 1:], x[i + 1:])) / U_Matrix[i, i]
257
+
258
+ return x
openum/NumEigenv.py ADDED
@@ -0,0 +1,142 @@
1
+ import numpy as np
2
+
3
+ def matrix_vector(N: np.ndarray, Matrix: np.ndarray, Vector: np.ndarray) -> np.ndarray:
4
+ """
5
+ Multiply a matrix by a vector.
6
+
7
+ Args:
8
+ N (int): Size of the matrix and vector.
9
+ Matrix (ndarray): Input matrix.
10
+ Vector (ndarray): Input vector.
11
+
12
+ Returns:
13
+ ndarray: Resulting vector after multiplication.
14
+ """
15
+ Solution = np.zeros(N)
16
+ for i in range(N):
17
+ for j in range(N):
18
+ Solution[i] += Matrix[i][j] * Vector[j]
19
+ return Solution
20
+
21
+ def Eigenvalue_calc(mat: np.ndarray, vec: np.ndarray, Iteration: int) -> tuple[float, np.ndarray]:
22
+ """
23
+ Compute an eigenvalue and eigenvector using power iteration.
24
+
25
+ Args:
26
+ mat (ndarray): Input matrix.
27
+ vec (ndarray): Initial vector.
28
+ Iteration (int): Number of iterations.
29
+
30
+ Returns:
31
+ tuple: Eigenvalue and eigenvector.
32
+ """
33
+ size = mat.shape[0]
34
+
35
+ for _ in range(Iteration):
36
+ vec = matrix_vector(size, mat, vec)
37
+ vec = vec / np.linalg.norm(vec) # Normalize the vector to prevent overflow
38
+
39
+ eigenvalue = np.dot(matrix_vector(size, mat, vec), vec) / np.dot(vec, vec)
40
+ eigenvector = vec
41
+ return eigenvalue, eigenvector
42
+
43
+ def Eigenvalues(mat: np.ndarray, vec: np.ndarray, Iteration: int)-> tuple[list[float], list[np.ndarray]]:
44
+ """
45
+ Compute eigenvalues and eigenvectors of a matrix and its inverse.
46
+
47
+ Args:
48
+ mat (ndarray): Input matrix.
49
+ vec (ndarray): Initial vector.
50
+ Iteration (int): Number of iterations.
51
+
52
+ Returns:
53
+ tuple: List of eigenvalues and eigenvectors.
54
+ """
55
+ eigenvalues = []
56
+ eigenvectors = []
57
+
58
+ try:
59
+ inv_mat = np.linalg.inv(mat)
60
+ for matrix in [mat, inv_mat]:
61
+ eigenvalue, eigenvector = Eigenvalue_calc(matrix, vec, Iteration)
62
+ eigenvalues.append(eigenvalue)
63
+ eigenvectors.append(eigenvector)
64
+ except np.linalg.LinAlgError:
65
+ eigenvalue, eigenvector = Eigenvalue_calc(mat, vec, Iteration)
66
+ eigenvalues.append(eigenvalue)
67
+ eigenvectors.append(eigenvector)
68
+
69
+ return eigenvalues, eigenvectors
70
+
71
+ def Aitken(Eigenvalues: list[float])->float:
72
+ """
73
+ Apply Aitken's delta-squared process for accelerated convergence.
74
+
75
+ Args:
76
+ Eigenvalues (list): List of computed eigenvalues.
77
+
78
+ Returns:
79
+ float: Accelerated eigenvalue.
80
+ """
81
+ if len(Eigenvalues) < 3:
82
+ return Eigenvalues[-1]
83
+ else:
84
+ x_n, x_n1, x_n2 = Eigenvalues[-3], Eigenvalues[-2], Eigenvalues[-1]
85
+ return x_n - (x_n1 - x_n) ** 2 / (x_n2 - 2 * x_n1 + x_n)
86
+
87
+ def eigenvalue_calc_aitken(mat: np.ndarray, vec: np.ndarray, Iteration: int)->tuple[float, np.ndarray]:
88
+ """
89
+ Compute an eigenvalue and eigenvector using Aitken's method.
90
+
91
+ Args:
92
+ mat (ndarray): Input matrix.
93
+ vec (ndarray): Initial vector.
94
+ Iteration (int): Number of iterations.
95
+
96
+ Returns:
97
+ tuple: Eigenvalue and eigenvector.
98
+ """
99
+ EVArray = []
100
+ size = mat.shape[0]
101
+
102
+ for i in range(Iteration):
103
+ vec = matrix_vector(size, mat, vec)
104
+ vec = vec / np.linalg.norm(vec)
105
+ eigenvalue = np.dot(matrix_vector(size, mat, vec), vec) / np.dot(vec, vec)
106
+ EVArray.append(eigenvalue)
107
+
108
+ Aitken_Eigenvalue = Aitken(EVArray)
109
+
110
+ if i >= 3 and abs(Aitken_Eigenvalue - EVArray[-1]) < 1e-8:
111
+ eigenvalue = Aitken_Eigenvalue
112
+ break
113
+
114
+ return eigenvalue, vec
115
+
116
+ def Eigenvalues_Aitken(mat: np.ndarray, vec: np.ndarray, Iteration: int)-> tuple[list[float],list[np.ndarray]]:
117
+ """
118
+ Compute eigenvalues and eigenvectors using Aitken's method.
119
+
120
+ Args:
121
+ mat (ndarray): Input matrix.
122
+ vec (ndarray): Initial vector.
123
+ Iteration (int): Number of iterations.
124
+
125
+ Returns:
126
+ tuple: List of eigenvalues and eigenvectors.
127
+ """
128
+ eigenvalues = []
129
+ eigenvectors = []
130
+
131
+ try:
132
+ inv_mat = np.linalg.inv(mat)
133
+ for matrix in [mat, inv_mat]:
134
+ eigenvalue, eigenvector = eigenvalue_calc_aitken(matrix, vec, Iteration)
135
+ eigenvalues.append(eigenvalue)
136
+ eigenvectors.append(eigenvector)
137
+ except np.linalg.LinAlgError:
138
+ eigenvalue, eigenvector = eigenvalue_calc_aitken(mat, vec, Iteration)
139
+ eigenvalues.append(eigenvalue)
140
+ eigenvectors.append(eigenvector)
141
+
142
+ return eigenvalues, eigenvectors