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