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
109 lines
3.9 KiB
Python
109 lines
3.9 KiB
Python
import os
|
|
|
|
import numpy as np
|
|
import scipy.io
|
|
|
|
|
|
class MatrixGenerator:
|
|
@staticmethod
|
|
def generate_spd_matrix(n: int) -> np.ndarray:
|
|
A = np.random.rand(n, n)
|
|
return np.dot(A, A.T) + n * MatrixGenerator.generate_identity_matrix(
|
|
n
|
|
) # Adding n*I ensures positive definiteness
|
|
|
|
@staticmethod
|
|
def generate_identity_matrix(size):
|
|
return np.eye(size)
|
|
|
|
@staticmethod
|
|
def generate_alternating_vector(size):
|
|
return np.tile([1, 2], int(np.ceil(size / 2)))[:size]
|
|
|
|
@staticmethod
|
|
def get_matrix_from_file(file_path, problem):
|
|
mat_contents = scipy.io.loadmat(file_path)
|
|
problem_record = mat_contents["Problem"][0][0]
|
|
A = problem_record[problem]
|
|
if scipy.sparse.issparse(A):
|
|
A_dense = A.todense()
|
|
else:
|
|
A_dense = A
|
|
return np.array(A_dense)
|
|
|
|
@staticmethod
|
|
def generate_matrix_and_vector(type, size=None):
|
|
if type == "spd":
|
|
if size is None:
|
|
raise ValueError("Size must be provided for SPD matrix generation.")
|
|
try:
|
|
matrix, vector, lambda_min, lambda_max = MatrixGenerator.load_from_file(
|
|
"spd_" + str(size) + ".npz"
|
|
)
|
|
except FileNotFoundError:
|
|
matrix = MatrixGenerator.generate_spd_matrix(size)
|
|
vector = np.random.uniform(-1, 1, size)
|
|
lambda_min, lambda_max = MatrixGenerator.calculate_eigenvalues(matrix)
|
|
MatrixGenerator.save_to_file(
|
|
matrix, vector, lambda_min, lambda_max, "spd_" + str(size) + ".npz"
|
|
)
|
|
elif type == "nemeth12":
|
|
try:
|
|
matrix, vector, lambda_min, lambda_max = MatrixGenerator.load_from_file(
|
|
"nemeth12.npz"
|
|
)
|
|
except FileNotFoundError:
|
|
matrix = -1 * MatrixGenerator.get_matrix_from_file("nemeth12.mat", 1)
|
|
size = matrix.shape[0]
|
|
vector = MatrixGenerator.generate_alternating_vector(size)
|
|
lambda_min, lambda_max = MatrixGenerator.calculate_eigenvalues(matrix)
|
|
MatrixGenerator.save_to_file(
|
|
matrix, vector, lambda_min, lambda_max, "nemeth12.npz"
|
|
)
|
|
elif type == "poli3":
|
|
try:
|
|
matrix, vector, lambda_min, lambda_max = MatrixGenerator.load_from_file(
|
|
"poli3.npz"
|
|
)
|
|
except FileNotFoundError:
|
|
matrix = MatrixGenerator.get_matrix_from_file("poli3.mat", 2)
|
|
size = matrix.shape[0]
|
|
vector = MatrixGenerator.generate_alternating_vector(size)
|
|
lambda_min, lambda_max = MatrixGenerator.calculate_eigenvalues(matrix)
|
|
MatrixGenerator.save_to_file(
|
|
matrix, vector, lambda_min, lambda_max, "poli3.npz"
|
|
)
|
|
else:
|
|
raise ValueError(
|
|
"Invalid type specified. Choose 'spd', 'nemeth12', or 'poli3'."
|
|
)
|
|
|
|
return matrix, vector, lambda_min, lambda_max
|
|
|
|
@staticmethod
|
|
def calculate_eigenvalues(A):
|
|
eigenvalues = np.linalg.eigvals(A)
|
|
lambda_min = np.min(eigenvalues)
|
|
lambda_max = np.max(eigenvalues)
|
|
return lambda_min, lambda_max
|
|
|
|
@staticmethod
|
|
def save_to_file(matrix, vector, lambda_min, lambda_max, file_path) -> None:
|
|
np.savez(
|
|
file_path,
|
|
matrix=matrix,
|
|
vector=vector,
|
|
lambda_min=lambda_min,
|
|
lambda_max=lambda_max,
|
|
)
|
|
|
|
def load_from_file(file_path):
|
|
if not os.path.exists(file_path):
|
|
raise FileNotFoundError(f"The file {file_path} does not exist.")
|
|
data = np.load(file_path)
|
|
matrix = data["matrix"]
|
|
vector = data["vector"]
|
|
lambda_min = data["lambda_min"]
|
|
lambda_max = data["lambda_max"]
|
|
return matrix, vector, lambda_min, lambda_max
|