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