From 875c71a0e81b07bb536a0f2615933ac5e0688a07 Mon Sep 17 00:00:00 2001 From: Bazunov Andrew Igorevich <35404933+viltskaa@users.noreply.github.com> Date: Thu, 28 Nov 2024 23:56:00 +0400 Subject: [PATCH 1/2] ready --- bazunov_andrew_lab_6/README.md | 55 +++++++++++ bazunov_andrew_lab_6/main.py | 26 +++++ bazunov_andrew_lab_6/matrix.py | 171 +++++++++++++++++++++++++++++++++ 3 files changed, 252 insertions(+) create mode 100644 bazunov_andrew_lab_6/README.md create mode 100644 bazunov_andrew_lab_6/main.py create mode 100644 bazunov_andrew_lab_6/matrix.py diff --git a/bazunov_andrew_lab_6/README.md b/bazunov_andrew_lab_6/README.md new file mode 100644 index 0000000..3ad50f6 --- /dev/null +++ b/bazunov_andrew_lab_6/README.md @@ -0,0 +1,55 @@ +# Распределенные вычисления и приложения Л6 + +## _Автор Базунов Андрей Игревич ПИбд-42_ + +--- +> ### Задание +> - Кратко: реализовать нахождение детерминанта квадратной матрицы. Что такое детерминант матрицы (или определитель) +> - Подробно: в лабораторной работе требуется сделать два алгоритма: обычный и параллельный (задание со * - реализовать это в рамках одного алгоритма). В параллельном алгоритме предусмотреть ручное задание количества потоков (число потоков = 1 как раз и реализует задание со *), каждый из которых будет выполнять нахождение отдельной группы множителей. +--- + +## Алгоритм: + +### Функция вычисления определителя матрицы + +
+Код + +```python +def minor(matrix, i, j): + return [row[:j] + row[j + 1:] for row in (matrix[:i] + matrix[i + 1:])] + + +def determinant(matrix: list, threads=1) -> float: + if len(matrix) == 1: + return matrix[0][0] + elif len(matrix) == 2: + return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0] + + det = 0 + futures = [] + with ThreadPoolExecutor(max_workers=threads) as executor: + for col in range(len(matrix)): + cofactor = (-1) ** col * matrix[0][col] + futures.append(executor.submit(lambda m, cof: cof * determinant(m), minor(matrix, 0, col), cofactor)) + + for future in futures: + det += future.result() + + return det +``` + +
+ + +| Размер матрицы | 1 Поток (сек) | 5 Потоков (сек) | 20 Потоков (сек) | +|----------------|---------------|-----------------|------------------| +| 1x1 | 0.0 | 0.0 | 0.0 | +| 2x2 | 0.0 | 0.0 | 0.0 | +| 3x3 | 0.00018 | 0.00019 | 0.00017 | +| 4x4 | 0.00061 | 0.00072 | 0.0007 | +| 5x5 | 0.00247 | 0.00359 | 0.00336 | +| 6x6 | 0.01306 | 0.01931 | 0.02129 | +| 7x7 | 0.08692 | 0.14062 | 0.15682 | +| 8x8 | 0.59383 | 1.03971 | 1.27658 | +| 9x9 | 5.23021 | 9.72031 | 11.71564 | \ No newline at end of file diff --git a/bazunov_andrew_lab_6/main.py b/bazunov_andrew_lab_6/main.py new file mode 100644 index 0000000..df7638d --- /dev/null +++ b/bazunov_andrew_lab_6/main.py @@ -0,0 +1,26 @@ +import time +from collections.abc import Callable +from random import random + +from matrix import Matrix + +_THREADS = 20 + + +def measure_time(func: Callable, *args) -> float: + t1 = time.process_time() + func(*args) + t2 = time.process_time() + return round(t2 - t1, 5) + + +tests = [i for i in range(1, 10)] + +for test in tests: + mt1 = Matrix(size=test, suplyer=random) + + t1 = measure_time(lambda: mt1.det()) + t5 = measure_time(lambda: mt1.det(threads=5)) + t20 = measure_time(lambda: mt1.det(threads=20)) + + print(f"|{f'{test}x{test}':<16}|{t1:^11}|{t5:^11}|{t20:^12}|") \ No newline at end of file diff --git a/bazunov_andrew_lab_6/matrix.py b/bazunov_andrew_lab_6/matrix.py new file mode 100644 index 0000000..be6f706 --- /dev/null +++ b/bazunov_andrew_lab_6/matrix.py @@ -0,0 +1,171 @@ +import itertools +from collections.abc import Callable +from concurrent.futures import ThreadPoolExecutor +from queue import Queue +from typing import Tuple, List +import numpy as np + +_SUPLYER_TYPE = Callable[[], int | float] | int | float +_QUEUE_TYPE = Queue[Tuple[List[float | int], List[float | int], int]] + + +class Matrix: + def __init__(self, size: int, suplyer: _SUPLYER_TYPE = 0): + self.__size = size + self.__matrix = self._generate_matrix(suplyer) + + def _generate_matrix(self, suplyer: _SUPLYER_TYPE): + if suplyer: + match suplyer: + case int() | float(): + return [[suplyer for _ in range(self.__size)] for _ in range(self.__size)] + case Callable(): + return [[suplyer() for _ in range(self.__size)] for _ in range(self.__size)] + return [[0 for _ in range(self.__size)] for _ in range(self.__size)] + + def from_flat(self, numbers: List[int | float]): + if len(numbers) != self.__size ** 2: + raise Exception(f"Invalid matrix size {self.__size} ^ 2 != {len(numbers)}") + x, y = 0, 0 + for number in numbers: + self.__matrix[y][x] = number + x += 1 + if x >= self.__size: + x = 0 + y += 1 + + @property + def rows(self): + return self.__matrix + + @property + def columns(self): + return [[self.__matrix[i][j] for i in range(self.__size)] for j in range(self.__size)] + + @property + def size(self): + return self.__size + + @staticmethod + def random(*, size: int): + import random + return Matrix(size=size, suplyer=random.random) + + def to_numpy(self): + return np.array(self.__matrix) + + def __eq__(self, other): + return (isinstance(other, Matrix) + and self.__size == other.__size) + + def __str__(self): + return f"Matrix {self.__size}x{self.__size} \n" + "\n".join([str( + " ".join([f"{round(element, 3):<10}" for element in row]) + ) for row in self.__matrix]) + + def __iter__(self): + return iter(self.__matrix) + + def __getitem__(self, index): + return self.__matrix[index] + + def __mul__(self, other): + match other: + case Matrix(): + return mul_matrixs(self, other) + case tuple(): + other_matrix, count_threads = other + return mul_matrixs(self, other_matrix, count_threads) + return None + + def toLU(self, threads: int = 1): + L = Matrix(size=self.__size, suplyer=lambda: 0) + U = Matrix(size=self.__size, suplyer=lambda: 0) + n = self.__size + + for i in range(n): + for k in range(i, n): + sum_upper = sum(L[i][j] * U[j][k] for j in range(i)) + U[i][k] = self.__matrix[i][k] - sum_upper + + L[i][i] = 1 + for k in range(i + 1, n): + sum_lower = sum(L[k][j] * U[j][i] for j in range(i)) + L[k][i] = (self.__matrix[k][i] - sum_lower) / U[i][i] + + return L, U + + def triangle_det(self): + det = 1 + for i in range(self.__size): + det *= self.__matrix[i][i] + + return det + + def det(self, threads=1) -> float: + return determinant(self.__matrix, threads) + + +def minor(matrix, i, j): + return [row[:j] + row[j + 1:] for row in (matrix[:i] + matrix[i + 1:])] + + +def determinant(matrix: list, threads=1) -> float: + if len(matrix) == 1: + return matrix[0][0] + elif len(matrix) == 2: + return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0] + + det = 0 + futures = [] + with ThreadPoolExecutor(max_workers=threads) as executor: + for col in range(len(matrix)): + cofactor = (-1) ** col * matrix[0][col] + futures.append(executor.submit(lambda m, cof: cof * determinant(m), minor(matrix, 0, col), cofactor)) + + for future in futures: + det += future.result() + + return det + + +def mul_row_and_column_in_thread(queue: _QUEUE_TYPE) -> list[tuple[int | float, int]]: + result = [] + while queue.qsize(): + local_result = 0 + row, column, place = queue.get() + for k in range(len(row)): + local_result += row[k] * column[k] + result.append((local_result, place)) + + return result + + +def mul_matrixs(m1: Matrix, m2: Matrix, threads: int = 0): + if m1.size != m2.size: + return None + + if threads == 0: + threads = 1 + + result = Matrix(size=m1.size, suplyer=0) + + thread_queues = [Queue() for _ in range(threads)] + thread_iterator = 0 + + for row_m1, column_m2 in itertools.product(m1.rows, m2.columns): + thread_queues[thread_iterator].put((row_m1, column_m2, thread_iterator)) + thread_iterator += 1 + if thread_iterator >= threads: + thread_iterator = 0 + + with ThreadPoolExecutor(max_workers=threads) as executor: + flat = [] + + for item in executor.map(mul_row_and_column_in_thread, thread_queues): + flat += item + + flat.sort(key=lambda x: x[1]) + result.from_flat([*map(lambda x: x[0], flat)]) + + return result From a088530e65f32a50896ea22ee17bfab9a4265058 Mon Sep 17 00:00:00 2001 From: viltskaa Date: Fri, 20 Dec 2024 01:00:01 +0400 Subject: [PATCH 2/2] =?UTF-8?q?=D0=92=D0=B8=D0=B4=D0=B5=D0=BE=20=D0=BE?= =?UTF-8?q?=D1=82=D1=87=D0=B5=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bazunov_andrew_lab_6/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bazunov_andrew_lab_6/README.md b/bazunov_andrew_lab_6/README.md index 3ad50f6..92dfe22 100644 --- a/bazunov_andrew_lab_6/README.md +++ b/bazunov_andrew_lab_6/README.md @@ -52,4 +52,6 @@ def determinant(matrix: list, threads=1) -> float: | 6x6 | 0.01306 | 0.01931 | 0.02129 | | 7x7 | 0.08692 | 0.14062 | 0.15682 | | 8x8 | 0.59383 | 1.03971 | 1.27658 | -| 9x9 | 5.23021 | 9.72031 | 11.71564 | \ No newline at end of file +| 9x9 | 5.23021 | 9.72031 | 11.71564 | + +Видео отчет: https://vkvideo.ru/video236673313_456239579 \ No newline at end of file