mirror of
https://github.com/kuhyx/WUT_Computer_Science.git
synced 2026-11-10 18:47:11 +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
135 lines
4.2 KiB
Python
135 lines
4.2 KiB
Python
import logging
|
|
|
|
import numpy as np
|
|
import pytest
|
|
from matrix_generator import MatrixGenerator
|
|
from processing_type import ProcessingType
|
|
from richardson_method import RichardsonMethod
|
|
from threads_indep import RichardsonMethodThreads
|
|
from time_measurement import tests_time, time_measurement
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def calculate_norm_numpy(I, w, A):
|
|
difference = I - w * A
|
|
return np.linalg.norm(difference)
|
|
|
|
|
|
def calculate_eigenvalues(A):
|
|
eigenvalues = np.linalg.eigvals(A)
|
|
lambda_min = np.min(eigenvalues)
|
|
lambda_max = np.max(eigenvalues)
|
|
return lambda_min, lambda_max
|
|
|
|
|
|
def calcualte_norm_from_matrix_numpy(A, n):
|
|
lambda_min, lambda_max = calculate_eigenvalues(A)
|
|
omega = 2 / (lambda_min + lambda_max)
|
|
I = np.eye(n)
|
|
return calculate_norm_numpy(I, omega, A)
|
|
|
|
|
|
@time_measurement(tests_time)
|
|
def solution_lib(A, b):
|
|
return np.linalg.solve(A, b)
|
|
|
|
|
|
@pytest.mark.parametrize("n", [2, 5, 10, 50, 100, 300, 500, 750, 1000, 5000, 10000])
|
|
@pytest.mark.parametrize(
|
|
"processing_type",
|
|
[
|
|
ProcessingType.SEQUENTIAL,
|
|
ProcessingType.THREADS,
|
|
ProcessingType.PROCESSES,
|
|
ProcessingType.DISTRIBUTED_ARRAYS,
|
|
],
|
|
)
|
|
@pytest.mark.parametrize("matrix_type", ["spd", "nemeth12", "poli3"])
|
|
def test_richardson_vs_cg(
|
|
n: int, processing_type: ProcessingType, matrix_type: str, capsys
|
|
) -> None:
|
|
logger.info("matrix type: %s", matrix_type)
|
|
logger.info("matrix size: %s", n if matrix_type == "spd" else "fixed")
|
|
tolerance = 8e-3
|
|
max_iterations = 100
|
|
if matrix_type in ["nemeth12", "poli3"] and n != 2:
|
|
pytest.skip(
|
|
"Fixed matrix size for nemeth12 and poli3, skipping redundant runs."
|
|
)
|
|
|
|
if matrix_type == "spd":
|
|
A, b, lambda_min, lambda_max = MatrixGenerator.generate_matrix_and_vector(
|
|
"spd", size=n
|
|
)
|
|
elif matrix_type == "poli3":
|
|
A, b, lambda_min, lambda_max = MatrixGenerator.generate_matrix_and_vector(
|
|
"poli3"
|
|
)
|
|
elif matrix_type == "nemeth12":
|
|
A, b, lambda_min, lambda_max = MatrixGenerator.generate_matrix_and_vector(
|
|
"nemeth12"
|
|
)
|
|
else:
|
|
raise ValueError(
|
|
"Invalid matrix type specified. Choose 'spd', 'poli3', or 'nemeth12'."
|
|
)
|
|
|
|
solution_richardson, info_richardson = None, None
|
|
|
|
if processing_type != ProcessingType.THREADS:
|
|
richardson_solver = RichardsonMethod(
|
|
processing_type,
|
|
A,
|
|
b,
|
|
lambda_min,
|
|
lambda_max,
|
|
max_iterations,
|
|
size=A.shape[0],
|
|
tol=1e-7,
|
|
)
|
|
with capsys.disabled():
|
|
solution_richardson, info_richardson = richardson_solver.solve()
|
|
else:
|
|
with capsys.disabled():
|
|
solution_richardson, info_richardson = RichardsonMethodThreads(
|
|
A, b, lambda_min, lambda_max, max_iterations, tol=1e-7
|
|
)
|
|
|
|
# Przechwytywanie wyjścia po solve
|
|
captured = capsys.readouterr()
|
|
logger.info("Captured output: %s", captured.out)
|
|
|
|
solution = solution_lib(A, b)
|
|
|
|
assert_converged(solution_richardson, info_richardson, solution, tolerance, A, n)
|
|
|
|
|
|
def assert_converged(
|
|
solution_richardson, info_richardson, solution, tolerance, A, n
|
|
) -> None:
|
|
if info_richardson == "Richardson method for those values will NOT converge":
|
|
numpy_norm = calcualte_norm_from_matrix_numpy(A, n)
|
|
logger.info(
|
|
"Numpy norm: %s Richardson norm: %s", numpy_norm, solution_richardson
|
|
)
|
|
assert False, "Richardson did not converge"
|
|
else:
|
|
difference = np.linalg.norm(solution_richardson - solution)
|
|
logger.info("Difference between Richardson and numpy solutions: %s", difference)
|
|
if difference < tolerance:
|
|
logger.info(
|
|
"Both Richardson and numpy method converged and calculated correct values."
|
|
)
|
|
else:
|
|
logger.info("Solution numpy:\n %s", solution)
|
|
logger.info("Solution Richardson:\n %s", solution_richardson)
|
|
assert difference < tolerance, (
|
|
f"The solutions are different! Difference: {difference:.8f}"
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
logging.basicConfig(format="%(message)s", level=logging.INFO)
|
|
pytest.main()
|