mirror of
https://github.com/kuhyx/WUT_Computer_Science.git
synced 2026-11-10 20:47:09 +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
119 lines
4.1 KiB
Python
119 lines
4.1 KiB
Python
import gc
|
|
import logging
|
|
import time
|
|
|
|
import linear_algebra_utils as linAlg
|
|
from processing_type import ProcessingType
|
|
from time_measurement import tests_time, time_accumulator, time_measurement
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class RichardsonMethod:
|
|
@time_measurement(time_accumulator)
|
|
def __init__(
|
|
self,
|
|
method: ProcessingType,
|
|
A,
|
|
b,
|
|
lambda_min,
|
|
lambda_max,
|
|
max_iterations,
|
|
size: int,
|
|
x0=None,
|
|
tol=1e-5,
|
|
) -> None:
|
|
self.LinAlg = self.assign_LinAlgType(method)
|
|
self.A = A
|
|
self.b = b
|
|
self.x0 = x0 if x0 is not None else [0.0] * len(b)
|
|
self.max_iterations = max_iterations
|
|
self.tol = tol
|
|
self.lambda_min = lambda_min
|
|
self.lambda_max = lambda_max
|
|
if self.lambda_min < 0:
|
|
raise ValueError("Matrix A is not positive semi-definite.")
|
|
self.omega = RichardsonMethod.calculate_omega(self.lambda_min, self.lambda_max)
|
|
|
|
@staticmethod
|
|
def calculate_omega(lambda_min, lambda_max):
|
|
return 2 / (lambda_min + lambda_max)
|
|
|
|
@staticmethod
|
|
def convergence_norm(LinAlgType, A, omega, I) -> bool:
|
|
wA = LinAlgType.matrix_scalar_multiply(A, omega)
|
|
IMinuswA = LinAlgType.matrix_matrix_subtraction(I, wA)
|
|
return LinAlgType.matrix_norm(IMinuswA)
|
|
|
|
@staticmethod
|
|
def assign_LinAlgType(method):
|
|
methods = {
|
|
ProcessingType.SEQUENTIAL: linAlg.SequentialLinearAlgebraUtils,
|
|
ProcessingType.THREADS: linAlg.ThreadsLinearAlgebraUtils,
|
|
ProcessingType.PROCESSES: linAlg.ProcessLinearAlgebraUtils,
|
|
ProcessingType.DISTRIBUTED_ARRAYS: linAlg.DistributedArraysLinearAlgebraUtils,
|
|
}
|
|
|
|
try:
|
|
return methods[method]
|
|
except KeyError:
|
|
raise ValueError(
|
|
"Unknown method, please use 'SEQUENTIAL', 'THREADS' or 'PROCESSES'."
|
|
)
|
|
|
|
def solve(self):
|
|
gc.disable()
|
|
time_accumulator.total_time = 0
|
|
start = time.perf_counter()
|
|
x = self.x0[:]
|
|
|
|
for iteration in range(self.max_iterations):
|
|
Ax = self.LinAlg.matrix_vector_multiply(self.A, x)
|
|
residual = self.LinAlg.vector_vector_subtraction(self.b, Ax)
|
|
x = self.LinAlg.vector_vector_addition(
|
|
x, self.LinAlg.scalar_vector_multiply(self.omega, residual)
|
|
)
|
|
if linAlg.SequentialLinearAlgebraUtils.vector_norm(residual) < self.tol:
|
|
break
|
|
|
|
end = time.perf_counter()
|
|
total_time = end - start
|
|
gc.enable()
|
|
|
|
match self.LinAlg:
|
|
case linAlg.SequentialLinearAlgebraUtils:
|
|
logger.info(
|
|
"Total: %ss, Tests time: %ss", total_time, tests_time.total_time
|
|
)
|
|
case linAlg.ThreadsLinearAlgebraUtils:
|
|
sequential_time = total_time - time_accumulator.total_time
|
|
logger.info(
|
|
"Total: %ss, Seq: %ss, Parallel (threads): %ss, Tests time: %ss",
|
|
total_time,
|
|
sequential_time,
|
|
time_accumulator.total_time,
|
|
tests_time.total_time,
|
|
)
|
|
case linAlg.ProcessLinearAlgebraUtils:
|
|
sequential_time = total_time - time_accumulator.total_time
|
|
logger.info(
|
|
"Total: %ss, Seq: %ss, Parallel (processes): %ss, Tests time: %ss",
|
|
total_time,
|
|
sequential_time,
|
|
time_accumulator.total_time,
|
|
tests_time.total_time,
|
|
)
|
|
case linAlg.DistributedArraysLinearAlgebraUtils:
|
|
sequential_time = total_time - time_accumulator.total_time
|
|
logger.info(
|
|
"Total: %ss, Seq: %ss, Parallel (distributed arrays): %ss, Tests time: %ss",
|
|
total_time,
|
|
sequential_time,
|
|
time_accumulator.total_time,
|
|
tests_time.total_time,
|
|
)
|
|
case _:
|
|
logger.info("Unhandled LinAlg type")
|
|
|
|
return x, 0
|