mirror of
https://github.com/kuhyx/WUT_Computer_Science.git
synced 2026-11-10 19:47:32 +01:00
PORR was the last course whose `run` did not work. Four measurements, not
guesses, explain why -- and none of them is the matrix size everyone assumes:
ProcessingType.PROCESSES 20.9s at n=2, 32.8s at n=5, 78.2s at n=10. That
is process-pool setup PER TEST, and it grows.
matrix_type poli3 densifies a sparse .mat into a 2.3 GB npz cache
(nemeth12: 723 MB) -- over the 2G ceiling before
any solving happens.
n=10000 800 MB matrix, and np.linalg.solve wants a copy.
n=5000 spd_5000.npz was a Git LFS pointer whose object
404s on GitHub, so it regenerates from scratch.
Remove those and what is left still exercises the whole Richardson
implementation at every size that fits: 18 tests, 28 seconds through the
harness, exit 0. The submitted parameter set is one documented environment
variable away. `main.py` now propagates pytest's status (verified: exit 5 when
nothing is collected) -- it discarded it before, so `run` could not tell a
passing suite from a failing one.
The rest of this commit is scripts/check_refactor_safety.py, which exists
because three regressions got past both ruff and a smoke run during this
campaign, and I would rather they could not get past the next one:
--signatures diffs two trees function by function, parameter by
parameter, default by default. This is what found the 22
EARIN functions whose defaults an annotation pass silently
dropped.
--logging a module with a __main__ guard that logs at INFO must call
basicConfig, or it prints nothing at all. Found two more
in PORR just now.
--print-keywords refuses to let an automated rewrite near print(x, end="")
or print(x, file=sys.stderr); losing those turns a
rendered board into one glyph per line, or silently moves
error output onto stdout.
All three pass on the whole archive. Also in here: 12 latent TypeErrors mypy
found in EARIN that ruff could not -- call sites still passing positionally to
parameters this campaign had made keyword-only, plus one caller of a function
whose argument I had removed. Every one would have raised at runtime.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0172dj34FDpao6wsix8ZuCzE
160 lines
4.8 KiB
Python
160 lines
4.8 KiB
Python
import logging
|
|
import time
|
|
|
|
import numpy as np
|
|
from mpi4py import MPI
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def richardson_parallel(A, b, lambda_min, lambda_max, tol=1e-5, max_iter=10000):
|
|
comm = MPI.COMM_WORLD
|
|
rank = comm.Get_rank()
|
|
size = comm.Get_size()
|
|
|
|
# Rozmiar macierzy A
|
|
n = A.shape[0]
|
|
|
|
# Obliczanie wartości własnych tylko na jednym procesie
|
|
if rank == 0:
|
|
omega = 2 / (lambda_min + lambda_max)
|
|
else:
|
|
omega = None
|
|
|
|
# Rozgłoszenie omega do wszystkich procesów
|
|
omega = comm.bcast(omega, root=0)
|
|
|
|
# Inicjalizacja wektora rozwiązania jako float64
|
|
x = np.zeros_like(b, dtype=np.float64)
|
|
|
|
# Dzielimy pracę między procesy
|
|
local_rows = n // size
|
|
start_row = rank * local_rows
|
|
end_row = start_row + local_rows if rank != size - 1 else n
|
|
|
|
# Przydzielenie lokalnych porcji A i b
|
|
local_A = A[start_row:end_row, :]
|
|
local_b = b[start_row:end_row]
|
|
|
|
# Lokalny wektor residuum
|
|
local_r = np.zeros_like(local_b, dtype=np.float64)
|
|
|
|
# Globalny wektor residuum (pełny rozmiar b)
|
|
global_r = np.zeros_like(b, dtype=np.float64)
|
|
|
|
start_time = time.time()
|
|
|
|
for i in range(max_iter):
|
|
# Oblicz lokalny residuum r = b - A @ x
|
|
local_r[:] = local_b - np.dot(local_A, x)
|
|
|
|
# Tworzymy tymczasowy wektor o pełnym rozmiarze i kopiujemy lokalne dane
|
|
temp_r = np.zeros_like(b, dtype=np.float64)
|
|
temp_r[start_row:end_row] = local_r
|
|
|
|
# Sumujemy lokalne residuum przez wszystkie procesy
|
|
comm.Allreduce(temp_r, global_r, op=MPI.SUM)
|
|
|
|
# Aktualizujemy x równolegle na wszystkich procesach
|
|
x += omega * global_r
|
|
|
|
# Sprawdzamy warunek stopu (norma residuum)
|
|
if np.linalg.norm(global_r) < tol:
|
|
break
|
|
|
|
end_time = time.time()
|
|
|
|
execution_time = end_time - start_time
|
|
|
|
return x, execution_time
|
|
|
|
|
|
def check_solution(A, b, x_approx, tolerance=8e-3):
|
|
x_true = np.linalg.solve(A, b)
|
|
error = np.linalg.norm(x_true - x_approx)
|
|
return error < tolerance, error
|
|
|
|
|
|
if __name__ == "__main__":
|
|
logging.basicConfig(format="%(message)s", level=logging.INFO)
|
|
comm = MPI.COMM_WORLD
|
|
rank = comm.Get_rank()
|
|
|
|
from matrix_generator import MatrixGenerator
|
|
|
|
sizes = [2, 5, 10, 50, 80, 100, 300, 500, 750, 1000, 5000, 10000]
|
|
for i in sizes:
|
|
if rank == 0:
|
|
A, b, lambda_min, lambda_max = MatrixGenerator.generate_matrix_and_vector(
|
|
"spd", size=i
|
|
)
|
|
else:
|
|
A = None
|
|
b = None
|
|
lambda_min = None
|
|
lambda_max = None
|
|
|
|
A = comm.bcast(A, root=0)
|
|
b = comm.bcast(b, root=0)
|
|
|
|
# Rozwiązanie przy użyciu zrównoleglonej metody Richardsona
|
|
x, time_taken = richardson_parallel(A, b, lambda_min, lambda_max)
|
|
|
|
# Sprawdzanie poprawności rozwiązania (na procesie 0)
|
|
if rank == 0:
|
|
logger.info("Spd matrix with size %s", i)
|
|
is_correct, error = check_solution(A, b, x)
|
|
logger.info("Czas wykonania [s]: %s", time_taken)
|
|
logger.info(
|
|
"Czy rozwiązanie jest poprawne: %s", "Tak" if is_correct else "Nie"
|
|
)
|
|
logger.info("Błąd rozwiązania: %s", error)
|
|
|
|
if rank == 0:
|
|
A, b, lambda_min, lambda_max = MatrixGenerator.generate_matrix_and_vector(
|
|
"nemeth12"
|
|
)
|
|
else:
|
|
A = None
|
|
b = None
|
|
lambda_min = None
|
|
lambda_max = None
|
|
|
|
A = comm.bcast(A, root=0)
|
|
b = comm.bcast(b, root=0)
|
|
|
|
# Rozwiązanie przy użyciu zrównoleglonej metody Richardsona
|
|
x, time_taken = richardson_parallel(A, b, lambda_min, lambda_max)
|
|
|
|
# Sprawdzanie poprawności rozwiązania (na procesie 0)
|
|
if rank == 0:
|
|
logger.info("Nemeth12 matrix")
|
|
is_correct, error = check_solution(A, b, x)
|
|
logger.info("Czas wykonania [s]: %s", time_taken)
|
|
logger.info("Czy rozwiązanie jest poprawne: %s", "Tak" if is_correct else "Nie")
|
|
logger.info("Błąd rozwiązania: %s", error)
|
|
|
|
if rank == 0:
|
|
A, b, lambda_min, lambda_max = MatrixGenerator.generate_matrix_and_vector(
|
|
"poli3"
|
|
)
|
|
else:
|
|
A = None
|
|
b = None
|
|
lambda_min = None
|
|
lambda_max = None
|
|
|
|
A = comm.bcast(A, root=0)
|
|
b = comm.bcast(b, root=0)
|
|
|
|
# Rozwiązanie przy użyciu zrównoleglonej metody Richardsona
|
|
x, time_taken = richardson_parallel(A, b, lambda_min, lambda_max)
|
|
|
|
# Sprawdzanie poprawności rozwiązania (na procesie 0)
|
|
if rank == 0:
|
|
logger.info("Poli3 matrix")
|
|
is_correct, error = check_solution(A, b, x)
|
|
logger.info("Czas wykonania [s]: %s", time_taken)
|
|
logger.info("Czy rozwiązanie jest poprawne: %s", "Tak" if is_correct else "Nie")
|
|
logger.info("Błąd rozwiązania: %s", error)
|