diff --git a/solve.eigs b/solve.eigs index bdedc8c..6d97afc 100644 --- a/solve.eigs +++ b/solve.eigs @@ -201,6 +201,14 @@ define pagerank(m, maxit) as: return [r, it] # ---- demo ---- +# zeros returns a numeric buffer since EigenScript#1093. Materialize only +# for display so both list and buffer-backed solvers print their components. +define display_vector(v) as: + local components is [] + for i in range of (len of v): + append of [components, v[i]] + return components + print of "=== iterative solvers: the predicate as a loop condition ===" print of "every loop runs until `report of change` is settled and HOLDs — the observer," print of "not a magnitude tolerance, decides when to stop. (iters include the hold)" @@ -210,9 +218,9 @@ A is [[4.0, -1.0, 0.0], [-1.0, 4.0, -1.0], [0.0, -1.0, 4.0]] bb is [3.0, 2.0, 3.0] rj is jacobi of [A, bb, 500] rg is gauss_seidel of [A, bb, 500] -print of f"Jacobi Ax=b -> {rj[0]}" +print of f"Jacobi Ax=b -> {display_vector of rj[0]}" print of f" {rj[1]} iters" -print of f"Gauss-Seidel Ax=b -> {rg[0]}" +print of f"Gauss-Seidel Ax=b -> {display_vector of rg[0]}" print of f" {rg[1]} iters (fresh values -> fewer than Jacobi)" print of "" @@ -224,6 +232,6 @@ print of "" # 3-node graph 0->{1,2}, 1->{2}, 2->{0}; column-stochastic. True stationary [0.4,0.2,0.4]. G is [[0.0, 0.0, 1.0], [0.5, 0.0, 0.0], [0.5, 1.0, 0.0]] rr is pagerank of [G, 500] -print of f"PageRank stationary distribution -> {rr[0]} ({rr[1]} iters)" +print of f"PageRank stationary distribution -> {display_vector of rr[0]} ({rr[1]} iters)" print of "" print of "DONE" diff --git a/tests/check_solver_output.py b/tests/check_solver_output.py new file mode 100644 index 0000000..bdc1803 --- /dev/null +++ b/tests/check_solver_output.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +"""Check the three numeric vectors printed by the standalone solver demo.""" +import ast +import math +import re +import sys + +output = sys.stdin.read() +# A @ [1, 1, 1] = b; G @ [0.4, 0.2, 0.4] = [0.4, 0.2, 0.4], sum = 1. +for label, expected in ( + ("Jacobi", (1.0, 1.0, 1.0)), + ("Gauss-Seidel", (1.0, 1.0, 1.0)), + ("PageRank", (0.4, 0.2, 0.4)), +): + rows = re.findall(r"^" + re.escape(label) + r"\s+[^\n]*?-> (\[[^\n]*?\])(?:\s|$)", output, re.M) + try: + if len(rows) != 1: + raise ValueError("expected one printed numeric vector") + values = ast.literal_eval(rows[0]) + if len(values) != 3 or any( + type(value) not in (int, float) + or not math.isfinite(value) + or not math.isclose(value, want, rel_tol=0.0, abs_tol=0.0001) + for value, want in zip(values, expected) + ): + raise ValueError(f"incorrect components: {values!r}") + except (ValueError, SyntaxError, TypeError) as error: + sys.exit(f"FAIL: {label}: {error}") + print(f"PASS: {label} prints all three converged components") diff --git a/tests/test_lab.sh b/tests/test_lab.sh index 093590e..f2d7778 100755 --- a/tests/test_lab.sh +++ b/tests/test_lab.sh @@ -35,8 +35,7 @@ SOUT=$("$EIGS" solve.eigs 2>&1) echo "$SOUT" echo "$SOUT" | grep -q "DONE" || { echo "FAIL: solve.eigs did not finish"; exit 1; } echo "$SOUT" | grep -q "dominant eigenvalue -> 2" || { echo "FAIL: power iteration eigenvalue wrong"; exit 1; } -# PageRank true stationary is [0.4, 0.2, 0.4] -echo "$SOUT" | grep -qE "0\.3999|0\.4000" || { echo "FAIL: PageRank did not reach the stationary distribution"; exit 1; } -# both linear solves reach ~[1,1,1] -echo "$SOUT" | grep -q "0.9999" || { echo "FAIL: Jacobi/Gauss-Seidel did not converge to the solution"; exit 1; } +# Check every component on its own named row; a number printed by another +# solver must not mask a missing distribution or an incorrect component. +printf '%s\n' "$SOUT" | python3 tests/check_solver_output.py echo "PASS: solvers converge via the predicate-driven loop (settled + hold)"