1 // This is core/vnl/algo/vnl_lsqr.cxx
2 //
3 // vnl_lsqr
4 // Author: David Capel
5 // Created: July 2000
6 //
7 //-----------------------------------------------------------------------------
8
9 #include <vector>
10 #include <iostream>
11 #include <algorithm>
12 #include "vnl_lsqr.h"
13 #include "vnl/vnl_vector_ref.h"
14
15 #include "lsqrBase.h"
16
17 class lsqrVNL : public lsqrBase
18 {
19 public:
lsqrVNL()20 lsqrVNL() { this->ls_ = nullptr; }
21
22 ~lsqrVNL() override = default;
23
24 /**
25 * computes y = y + A*x without altering x,
26 * where A is a matrix of dimensions A[m][n].
27 * The size of the vector x is n.
28 * The size of the vector y is m.
29 */
30 void
Aprod1(unsigned int m,unsigned int n,const double * x,double * y) const31 Aprod1(unsigned int m, unsigned int n, const double * x, double * y) const override
32 {
33 vnl_vector_ref<double> x_ref(n, const_cast<double *>(x));
34 vnl_vector_ref<double> y_ref(m, y);
35
36 vnl_vector_ref<double> tmp(m, rw);
37 this->ls_->multiply(x_ref, tmp);
38 y_ref += tmp;
39 }
40
41
42 /**
43 * computes x = x + A'*y without altering y,
44 * where A is a matrix of dimensions A[m][n].
45 * The size of the vector x is n.
46 * The size of the vector y is m.
47 */
48 void
Aprod2(unsigned int m,unsigned int n,double * x,const double * y) const49 Aprod2(unsigned int m, unsigned int n, double * x, const double * y) const override
50 {
51 vnl_vector_ref<double> x_ref(n, x);
52 vnl_vector_ref<double> y_ref(m, const_cast<double *>(y));
53
54 vnl_vector_ref<double> tmp(n, this->rw);
55 this->ls_->transpose_multiply(y_ref, tmp);
56 x_ref += tmp;
57 }
58
59 /** Set the linear system to be solved A*x = b. */
60 void
SetLinearSystem(vnl_linear_system * inls)61 SetLinearSystem(vnl_linear_system * inls)
62 {
63 this->ls_ = inls;
64 }
65
66 void
SetWorkingSpace(double * inrw)67 SetWorkingSpace(double * inrw)
68 {
69 this->rw = inrw;
70 }
71
72 private:
73 vnl_linear_system * ls_;
74
75 double * rw;
76 };
77
78
79 vnl_lsqr::~vnl_lsqr() = default;
80
81 // Requires number_of_residuals() of workspace in rw.
82 int
aprod_(const long * mode,const long * m,const long * n,double * x,double * y,long *,long *,long *,double * rw,void * userdata)83 vnl_lsqr::aprod_(const long * mode,
84 const long * m,
85 const long * n,
86 double * x,
87 double * y,
88 long * /*leniw*/,
89 long * /*lenrw*/,
90 long * /*iw*/,
91 double * rw,
92 void * userdata)
93 {
94 //
95 // THIS CODE IS DEPRECATED
96 // THE FUNCTIONALITY HAS BEEN MOVED TO THE lsqrVNL class above.
97 // THE FUNCTIONS IS CONSERVED HERE ONLY FOR BACKWARD COMPATIBILITY.
98 //
99 auto * self = static_cast<vnl_lsqr *>(userdata);
100
101 // If MODE = 1, compute y = y + A*x.
102 // If MODE = 2, compute x = x + A(transpose)*y.
103
104 vnl_vector_ref<double> x_ref(*n, x);
105 vnl_vector_ref<double> y_ref(*m, y);
106
107 if (*mode == 1)
108 {
109 vnl_vector_ref<double> tmp(*m, rw);
110 self->ls_->multiply(x_ref, tmp);
111 y_ref += tmp;
112 }
113 else
114 {
115 vnl_vector_ref<double> tmp(*n, rw);
116 self->ls_->transpose_multiply(y_ref, tmp);
117 x_ref += tmp;
118 }
119
120 return 0;
121 }
122
123 int
minimize(vnl_vector<double> & result)124 vnl_lsqr::minimize(vnl_vector<double> & result)
125 {
126 long m = ls_->get_number_of_residuals();
127 long n = ls_->get_number_of_unknowns();
128 double damp = 0;
129
130 // NOTE: rw is a scratch space used for both intermediate residual and unknown computations
131 std::vector<double> rw(std::max(m, n));
132 std::vector<double> v(n);
133 std::vector<double> se(n);
134
135 double atol = 0;
136 double btol = 0;
137 #ifdef THIS_CODE_IS_DISABLED_BECAUSE_THE_LSQR_CODE_FROM_NETLIB_WAS_COPYRIGHTED_BY_ACM
138 double conlim = 0;
139 long nout = -1;
140 double acond, rnorm, xnorm;
141 #endif
142 double anorm, arnorm;
143
144 vnl_vector<double> rhs(m);
145 ls_->get_rhs(rhs);
146
147 lsqrVNL solver;
148
149 solver.SetDamp(damp);
150 solver.SetLinearSystem(this->ls_);
151 solver.SetWorkingSpace(rw.data());
152 solver.SetMaximumNumberOfIterations(max_iter_);
153 solver.SetStandardErrorEstimates(se.data());
154 solver.SetToleranceA(atol);
155 solver.SetToleranceB(btol);
156
157 solver.Solve(m, n, rhs.data_block(), result.data_block());
158
159 #ifdef THIS_CODE_IS_DISABLED_BECAUSE_THE_LSQR_CODE_FROM_NETLIB_WAS_COPYRIGHTED_BY_ACM
160 v3p_netlib_lsqr_(&m,
161 &n,
162 aprod_,
163 &damp,
164 &leniw,
165 &lenrw,
166 iw,
167 &rw[0],
168 rhs.data_block(),
169 &v[0],
170 &w[0],
171 result.data_block(),
172 &se[0],
173 &atol,
174 &btol,
175 &conlim,
176 &max_iter_,
177 &nout,
178 &return_code_,
179 &num_iter_,
180 &anorm,
181 &acond,
182 &rnorm,
183 &arnorm,
184 &xnorm,
185 this);
186 #endif
187
188 resid_norm_estimate_ = solver.GetFinalEstimateOfNormRbar();
189 result_norm_estimate_ = solver.GetFinalEstimateOfNormOfX();
190 A_condition_estimate_ = solver.GetConditionNumberEstimateOfAbar();
191 return_code_ = solver.GetStoppingReason();
192 num_iter_ = solver.GetNumberOfIterationsPerformed();
193 anorm = solver.GetFrobeniusNormEstimateOfAbar();
194 arnorm = solver.GetFinalEstimateOfNormOfResiduals();
195
196 #ifdef THIS_CODE_IS_DISABLED_BECAUSE_THE_LSQR_CODE_FROM_NETLIB_WAS_COPYRIGHTED_BY_ACM
197 std::cerr << "A Fro norm estimate = " << anorm << std::endl
198 << "A condition estimate = " << acond << std::endl
199 << "Residual norm estimate = " << rnorm << std::endl
200 << "A'(Ax - b) norm estimate = " << arnorm << std::endl
201 << "x norm estimate = " << xnorm << std::endl;
202 #endif
203
204 // We should return the return code, as translate_return_code is public and
205 // it is very misleading that the return code from this function can't be fed
206 // into translate_return_code. (Brian Amberg)
207 return return_code_;
208 }
209
210 void
diagnose_outcome(std::ostream & os) const211 vnl_lsqr::diagnose_outcome(std::ostream & os) const
212 {
213 translate_return_code(os, return_code_);
214 os << __FILE__ " : residual norm estimate = " << resid_norm_estimate_ << std::endl
215 << __FILE__ " : result norm estimate = " << result_norm_estimate_ << std::endl
216 << __FILE__ " : condition no. estimate = " << A_condition_estimate_ << std::endl
217 << __FILE__ " : iterations = " << num_iter_ << std::endl;
218 }
219
220 void
translate_return_code(std::ostream & os,int rc)221 vnl_lsqr::translate_return_code(std::ostream & os, int rc)
222 {
223 const char * vnl_lsqr_reasons[] = { "x = 0 is the exact solution. No iterations were performed.",
224 "The equations A*x = b are probably compatible. "
225 "Norm(A*x - b) is sufficiently small, given the "
226 "values of ATOL and BTOL.",
227 "The system A*x = b is probably not compatible. "
228 "A least-squares solution has been obtained that is "
229 "sufficiently accurate, given the value of ATOL.",
230 "An estimate of cond(Abar) has exceeded CONLIM. "
231 "The system A*x = b appears to be ill-conditioned. "
232 "Otherwise, there could be an error in subroutine APROD.",
233 "The equations A*x = b are probably compatible. "
234 "Norm(A*x - b) is as small as seems reasonable on this machine.",
235 "The system A*x = b is probably not compatible. A least-squares "
236 "solution has been obtained that is as accurate as seems "
237 "reasonable on this machine.",
238 "Cond(Abar) seems to be so large that there is no point in doing further "
239 "iterations, given the precision of this machine. "
240 "There could be an error in subroutine APROD.",
241 "The iteration limit ITNLIM was reached." };
242
243 if (rc < 0 || rc > 7)
244 os << __FILE__ " : Illegal return code : " << rc << std::endl;
245 else
246 os << __FILE__ " : " << vnl_lsqr_reasons[rc] << std::endl;
247 }
248