I think I got it. MSE rules and this should be obvious had I been competent.
This is what @unlikely did in his question
Reap@LinearSolve[m, b, Method -> {"Krylov", "ResidualNormFunction" -> ((Sow[#, "residue_vector"]; Sow[Norm[#, 2]]) &) } ]
This should intuitively suffice the purpose in question, however does not.
I finished my mock-up that I think is mathematically equivalent to @user293787 's
ClearAll[Gravifer`myKrylovLinearSolve] Module[{symbol = Gravifer`myKrylovLinearSolve, $symbol = SparseArray`KrylovLinearSolve}, ((MessageName[Evaluate@symbol, #] = MessageName[Evaluate@$symbol, #]) &) /@ ToExpression[ FindList[ FileNameJoin[{System`Private`$MessagesDir, $Language, "Messages.m"}], ToString[$symbol]] , StandardForm, HoldForm][[All, 1, 1, -1]] ]; Options[Gravifer`myKrylovLinearSolve] ^= Options[SparseArray`KrylovLinearSolve]; SyntaxInformation[ Gravifer`myKrylovLinearSolve] ^= {"ArgumentsPattern" -> {_, _, OptionsPattern[]}, "OptionNames" -> Union[First /@ Options[Gravifer`myKrylovLinearSolve], First /@ Options[SparseArray`KrylovLinearSolve]]}; Gravifer`myKrylovLinearSolve[m_, b_, opt : OptionsPattern[SparseArray`KrylovLinearSolve]] := Module[{dim = Dimensions[m][[1]], maxiter = OptionValue[MaxIterations], tol = OptionValue[Tolerance], x0 = OptionValue["StartingVector"], resnorm = OptionValue["ResidualNormFunction"] /. Automatic -> Norm, CGfunc}, CGfunc = Function[{n, A, \[FormalB], \[FormalCapitalN], \[Epsilon], x0}, Module[{x = x0, r = \[FormalB] - A . x0, p = \[FormalB] - A . x0, \[Alpha], \[Beta]}, Do[If[resnorm[r] < \[Epsilon], Break[]]; If[p . A . p == 0, Message[Gravifer`myKrylovLinearSolve::krystg]; Break[]]; \[Alpha] = Norm[r]^2/p . A . p; x += \[Alpha] p; \[Beta] = Norm[r - A . (\[Alpha] p)]^2/ Norm[r]^2; r -= A . (\[Alpha] p); p = \[Beta] p + r, \[FormalCapitalN]]; If[resnorm[r] > \[Epsilon], Message[Gravifer`myKrylovLinearSolve::krymit, \ \[FormalCapitalN]]]; x ]]; If[Precision[{m, b}] == \[Infinity], Message[Gravifer`myKrylovLinearSolve::kryinfp]]; (* If[Head[m]==SparseArray\[And]m["Background"]!=0,Message[ Gravifer`myKrylovLinearSolve::kryinz,"m"]]; If[Head[b]==SparseArray\[And]b["Background"]!=0,Message[ Gravifer`myKrylovLinearSolve::kryinz,"b"]]; *) Switch[OptionValue[Method], Automatic | "ConjugateGradient", CGfunc[dim, m, b, maxiter /. Automatic -> n, tol /. Automatic -> 10^-12, x0 /. Automatic -> ConstantArray[0, dim]] ] ] /; And[CheckArguments[Gravifer`myKrylovLinearSolve[m, b, opt], 2], (If[MatrixQ[m], True, Message[Gravifer`myKrylovLinearSolve::matrix, m, 1]; If[MatrixQ[Evaluate@m], True, Message[Gravifer`myKrylovLinearSolve::krynfa, m, 1]]; False]), (VectorQ[b] \[Or] If[MatrixQ[b], True, Message[Gravifer`myKrylovLinearSolve::matrix, b, 2]; False]), ((OptionValue[MaxIterations] == Automatic) \[Or] If[IntegerQ@OptionValue[MaxIterations], True, Message[Gravifer`myKrylovLinearSolve::kryit, OptionValue[MaxIterations]]; False]), ((OptionValue["StartingVector"] == Automatic) \[Or] If[VectorQ[OptionValue["StartingVector"], NumberQ], True, Message[Gravifer`myKrylovLinearSolve::kryivec]; False]), Switch[OptionValue[Method], Automatic | "ConjugateGradient", True, "SteepestDescent" | "Lanczos" | "Arnoldi" | "MinRES" | "GMRES" | "CGR" | "CGNR" | "CGLE" | "QMR" | "BiCG" | "BiCGStab", Message[Gravifer`myKrylovLinearSolve::krynimp]; False, _, Message[Gravifer`myKrylovLinearSolve::kryme, OptionValue["Method"]]; False ] ]
On the cases from his answer,
SeedRandom[1]; dim = 2000; U = RandomVariate[CircularRealMatrixDistribution[dim]]; A = Transpose[U] . DiagonalMatrix[RandomReal[{1/100, 1}, dim]] . U; b = RandomReal[{-1, 1}, dim]; {sol, reap} = LinearSolve[A, b, Method -> {"Krylov", Method -> "ConjugateGradient", "ResidualNormFunction" -> ((Sow[Norm[#]]) &)}] // Reap; {mysol, myreap} = Gravifer`myKrylovLinearSolve[A, b, Method -> "ConjugateGradient", "ResidualNormFunction" -> ((Sow[Norm[#]]) &)] // Reap; ListLinePlot[{reap[[1]], myreap[[1]]}, ScalingFunctions -> "Log"] {sol, reap} = LinearSolve[N@A, N@b, Method -> {"Krylov", Method -> "ConjugateGradient", "ResidualNormFunction" -> ((Sow[Norm[#]]) &)}] // Reap; {mysol, myreap} = Gravifer`myKrylovLinearSolve[N@A, N@b, Method -> "ConjugateGradient", "ResidualNormFunction" -> ((Sow[Norm[#]]) &)] // Reap; ListLinePlot[{reap[[1]], myreap[[1]]}, ScalingFunctions -> "Log"]
The results are
![ListLinePlot[{reap[[1]], myreap[[1]]}, ScalingFunctions -> "Log"]](https://i.sstatic.net/9yjom.png)
So maybe as @unlikely indeed suggested in his post, OptionValue["ResidualNormFunction"] is not called once per iteration for the built-in function.
U = RandomVariate[CircularRealMatrixDistribution[3]]. $\endgroup$LinearSolve[m, b, Method -> {"Krylov", <suboptions>}]should take string option keys, but it also accepts symbol keys; is this intended? How can I do this in my custom functions? $\endgroup$