diff --git a/code/linear_algebra_utils.py b/code/linear_algebra_utils.py index 876533c3..800259cf 100644 --- a/code/linear_algebra_utils.py +++ b/code/linear_algebra_utils.py @@ -2,6 +2,7 @@ import math from abc import ABC, abstractmethod from concurrent.futures import ThreadPoolExecutor from functools import partial +from time_measurement import time_measurement, threads_time_accumulator class LinearAlgebraUtils(ABC): @staticmethod @@ -90,10 +91,9 @@ class SequentialLinearAlgebraUtils(ABC): return [x+y for x, y in zip(v1, v2)] @staticmethod - def scalar_vector_multiply(omega, vector): # na pewno scalar matrix? a nie scalar vector? + def scalar_vector_multiply(omega, vector): return [omega * x for x in vector] - @staticmethod def matrix_norm(A): return math.sqrt(sum(sum(element ** 2 for element in row) for row in A)) @@ -171,6 +171,7 @@ class ThreadsLinearAlgebraUtils(ABC): @staticmethod + @time_measurement(threads_time_accumulator) def dot_product(v1, v2): chunks = ThreadsLinearAlgebraUtils.divide_vectors_to_chunks(v1, v2) with ThreadPoolExecutor(max_workers=ThreadsLinearAlgebraUtils.NUM_THREADS) as executor: @@ -178,6 +179,7 @@ class ThreadsLinearAlgebraUtils(ABC): return sum(results) @staticmethod + @time_measurement(threads_time_accumulator) def matrix_vector_multiply(A, x): chunks = ThreadsLinearAlgebraUtils.divide_vector_or_matrix_to_chunks(A) with ThreadPoolExecutor(max_workers=ThreadsLinearAlgebraUtils.NUM_THREADS) as executor: @@ -186,6 +188,7 @@ class ThreadsLinearAlgebraUtils(ABC): return [item for sublist in results for item in sublist] @staticmethod + @time_measurement(threads_time_accumulator) def vector_norm(v): chunks = ThreadsLinearAlgebraUtils.divide_vector_or_matrix_to_chunks(v) @@ -198,6 +201,7 @@ class ThreadsLinearAlgebraUtils(ABC): return total_sum**0.5 @staticmethod + @time_measurement(threads_time_accumulator) def vector_scalar_divide(x, scalar): chunks = ThreadsLinearAlgebraUtils.divide_vector_or_matrix_to_chunks(x) @@ -206,6 +210,7 @@ class ThreadsLinearAlgebraUtils(ABC): return [item for sublist in results for item in sublist] @staticmethod + @time_measurement(threads_time_accumulator) def matrix_scalar_multiply(A, w): chunks = ThreadsLinearAlgebraUtils.divide_vector_or_matrix_to_chunks(A) with ThreadPoolExecutor(max_workers=ThreadsLinearAlgebraUtils.NUM_THREADS) as executor: @@ -213,6 +218,7 @@ class ThreadsLinearAlgebraUtils(ABC): return [item for sublist in results for item in sublist] @staticmethod + @time_measurement(threads_time_accumulator) def vector_vector_subtraction(v1, v2): chunks = ThreadsLinearAlgebraUtils.divide_vectors_to_chunks(v1, v2) with ThreadPoolExecutor(max_workers=ThreadsLinearAlgebraUtils.NUM_THREADS) as executor: @@ -221,6 +227,7 @@ class ThreadsLinearAlgebraUtils(ABC): @staticmethod + @time_measurement(threads_time_accumulator) def vector_vector_addition(v1, v2): chunks = ThreadsLinearAlgebraUtils.divide_vectors_to_chunks(v1, v2) with ThreadPoolExecutor(max_workers=ThreadsLinearAlgebraUtils.NUM_THREADS) as executor: @@ -228,6 +235,7 @@ class ThreadsLinearAlgebraUtils(ABC): return [item for sublist in results for item in sublist] @staticmethod + @time_measurement(threads_time_accumulator) def scalar_vector_multiply(omega, vector): chunks = ThreadsLinearAlgebraUtils.divide_vector_or_matrix_to_chunks(vector) with ThreadPoolExecutor(max_workers=ThreadsLinearAlgebraUtils.NUM_THREADS) as executor: @@ -236,6 +244,7 @@ class ThreadsLinearAlgebraUtils(ABC): return [item for sublist in results for item in sublist] @staticmethod + @time_measurement(threads_time_accumulator) def matrix_norm(A): chunks = ThreadsLinearAlgebraUtils.divide_vector_or_matrix_to_chunks(A) @@ -249,11 +258,13 @@ class ThreadsLinearAlgebraUtils(ABC): return math.sqrt(total_sum) @staticmethod + @time_measurement(threads_time_accumulator) def divide_matrixes_to_chunks(A, B): chunk_size = len(A) // ThreadsLinearAlgebraUtils.NUM_THREADS return [(A[i:i + chunk_size], B[i:i + chunk_size]) for i in range(0, len(A), chunk_size)] @staticmethod + @time_measurement(threads_time_accumulator) def matrix_matrix_subtraction(A, B): def subtract_chunk(pair): @@ -266,6 +277,7 @@ class ThreadsLinearAlgebraUtils(ABC): return [row for chunk in results for row in chunk] @staticmethod + @time_measurement(threads_time_accumulator) def gaussian_elimination(A, b): n = len(A) M = [row[:] for row in A] diff --git a/code/richardson_method.py b/code/richardson_method.py index da9ab4d1..e01ae8dd 100644 --- a/code/richardson_method.py +++ b/code/richardson_method.py @@ -4,6 +4,9 @@ from linear_algebra_utils import ThreadsLinearAlgebraUtils from eigenvalue_methods import EigenvalueMethods from matrix_generator import MatrixGenerator from processing_type import ProcessingType +from time_measurement import threads_time_accumulator +import time +import gc class RichardsonMethod: def __init__(self, method: ProcessingType, A, b, max_iterations, size: int, x0=None, tol=1e-5): @@ -47,6 +50,9 @@ class RichardsonMethod: raise ValueError("Unknown method, please use 'SEQUENTIAL' or 'THREADS'.") def solve(self): + gc.disable() + threads_time_accumulator.total_time = 0 + start = time.time() x = self.x0[:] #if RichardsonMethod.convergence_norm(self.LinAlg, self.A, self.omega, self.I) >= 1: # return RichardsonMethod.convergence_norm(self.A, self.omega, self.I), "Richardson method for those values will NOT converge", @@ -56,4 +62,13 @@ class RichardsonMethod: residual = self.LinAlg.vector_vector_subtraction(self.b, Ax) x = self.LinAlg.vector_vector_addition(x, self.LinAlg.scalar_vector_multiply(self.omega, residual)) + end = time.time() + total_time = end - start + gc.enable() + if(self.LinAlg == SequentialLinearAlgebraUtils): + print(f"Total: {total_time:.3e}s") + elif(self.LinAlg == ThreadsLinearAlgebraUtils): + sequential_time = total_time - threads_time_accumulator.total_time + print(f"Total: {total_time:.3e}s, Seq: {sequential_time:.3e}s, Parallel: {threads_time_accumulator.total_time:.3e}s") + return x, 0 diff --git a/code/tests.py b/code/tests.py index 87e30c88..f9d861e4 100644 --- a/code/tests.py +++ b/code/tests.py @@ -32,13 +32,21 @@ def calcualte_norm_from_matrix_numpy(A, n, max_iterations): @pytest.mark.parametrize("n", [2, 3, 4, 5, 10, 20, 50, 100]) @pytest.mark.parametrize("processing_type", [ProcessingType.SEQUENTIAL, ProcessingType.THREADS]) -def test_richardson_vs_cg(n: int, processing_type: ProcessingType): +def test_richardson_vs_cg(n: int, processing_type: ProcessingType, capsys): print("matrix size: ", n) tolerance = 1e-5 max_iterations=1000 A, b = MatrixGenerator.generate_random_matrix_and_vector(n) richardson_solver = RichardsonMethod(processing_type, A, b, max_iterations, size=n, tol=1e-7) - solution_richardson, info_richardson = richardson_solver.solve() + # solution_richardson, info_richardson = richardson_solver.solve() + + solution_richardson, info_richardson = None, None + with capsys.disabled(): + solution_richardson, info_richardson = richardson_solver.solve() + + # Przechwytywanie wyjścia po solve + captured = capsys.readouterr() + print("Captured output:", captured.out) solution_cg, info = cg(A, b) diff --git a/code/time_measurement.py b/code/time_measurement.py new file mode 100644 index 00000000..899ace07 --- /dev/null +++ b/code/time_measurement.py @@ -0,0 +1,24 @@ +import time +from functools import wraps + +class TimeAccumulator: + def __init__(self): + self.total_time = 0 + +threads_time_accumulator = TimeAccumulator() + +def time_measurement(accumulator): + def decorator(func): + @wraps(func) + def inner(*args, **kwargs): + start = time.time() + result = func(*args, **kwargs) + end = time.time() + accumulator.total_time += end - start + return result + return inner + return decorator + + + +