1 #include <cstdlib>
2 #include <iostream>
3 #include <algorithm>
4 #include "mbl_dyn_prog.h"
5 //:
6 // \file
7 
8 #ifdef _MSC_VER
9 #  include "vcl_msvc_warnings.h"
10 #endif
11 #include "vsl/vsl_indent.h"
12 #include "vsl/vsl_binary_io.h"
13 #include <vnl/io/vnl_io_matrix.h>
14 
15 //=======================================================================
16 
17 //=======================================================================
18 // Dflt ctor
19 //=======================================================================
20 
21 mbl_dyn_prog::mbl_dyn_prog() = default;
22 
23 //=======================================================================
24 // Destructor
25 //=======================================================================
26 
27 mbl_dyn_prog::~mbl_dyn_prog() = default;
28 
29 
30 //: Construct path from links_, assuming it ends at end_state
construct_path(std::vector<int> & x,int end_state)31 void mbl_dyn_prog::construct_path(std::vector<int>& x, int end_state)
32 {
33   unsigned int n = links_.rows();
34   int ** b_data = links_.get_rows()-1;  // So that b_data[i] corresponds to i-th row
35   if (x.size()!=n+1) x.resize(n+1);
36   int *x_data = &x[0];
37   x_data[n] = end_state;
38   for (unsigned int i=n; i>0; --i)
39     x_data[i-1] = b_data[i][x_data[i]];
40 }
41 
mbl_abs(int i)42 static inline int mbl_abs(int i) { return i>=0 ? i : -i; }
43 
44 //=======================================================================
45 //: Compute running costs for DP problem with costs W
46 //  Pair cost term:  C_i(x1,x2) = c(|x1-x2|)
47 //  Size of c indicates maximum displacement between neighbouring
48 //  states.
49 //  If first_state>=0 then the first is constrained to that index value
running_costs(const vnl_matrix<double> & W,const vnl_vector<double> & pair_cost,int first_state)50 void mbl_dyn_prog::running_costs(
51                const vnl_matrix<double>& W,
52                const vnl_vector<double>& pair_cost,
53                int first_state)
54 {
55   int n = W.rows();
56   int n_states = W.columns();
57   const double * const* W_data = W.data_array();
58   int max_d = pair_cost.size()-1;
59 
60    // On completion b(i,j) shows the best prior state (ie at i)
61    // leading to state j at time i+1
62   links_.resize(n-1,n_states);
63   int ** b_data = links_.get_rows()-1;
64      // So that b_data[i] corresponds to i-th row
65 
66     // ci(j) is total cost to get to current state j
67   running_cost_ = W.get_row(0);
68   double *ci = running_cost_.data_block();
69   next_cost_.set_size(n_states);
70   double *ci_new = next_cost_.data_block();
71 
72   for (int i=1;i<n;++i)
73   {
74     int *bi = b_data[i];
75     const double *wi = W_data[i];
76 
77     for (int j=0;j<n_states;++j)
78     {
79       // Evaluate best route to get to state j at time i
80       int k_best = 0;
81       double cost;
82       double wj = wi[j];
83       double cost_best;
84 
85       if (i==1 && first_state>=0)
86       {
87         // Special case: First point pinned down to first_pt
88         k_best = first_state;
89         int d = mbl_abs(j-k_best);
90         if (d>max_d) cost_best=9e9;
91         else
92                      cost_best = ci[k_best] + pair_cost[d]+ wj;
93       }
94       else
95       {
96         int klo = std::max(0,j-max_d);
97         int khi = std::min(n_states-1,j+max_d);
98         k_best=klo;
99         cost_best = ci[klo] + pair_cost[mbl_abs(j-klo)] + wj;
100         for (int k=klo+1;k<=khi;++k)
101         {
102           cost = ci[k] + pair_cost[mbl_abs(j-k)] + wj;
103           if (cost<cost_best)
104           {
105             cost_best=cost;
106             k_best = k;
107           }
108         }
109       }
110 
111       ci_new[j] = cost_best;
112       bi[j] = k_best;
113     }
114 
115     running_cost_=next_cost_;
116   }
117 }
118 
119 //=======================================================================
120 //: Compute running costs for DP problem with costs W
121 //  Pair cost term:  C_i(x1,x2) = c(i0+x2-x1)
122 //  Size of c indicates maximum displacement between neighbouring
123 //  states.
124 //  If first_state>=0 then the first is constrained to that index value
running_costs_asym(const vnl_matrix<double> & W,const vnl_vector<double> & pair_cost,int i0,int first_state)125 void mbl_dyn_prog::running_costs_asym(
126                const vnl_matrix<double>& W,
127                const vnl_vector<double>& pair_cost,
128                int i0, int first_state)
129 {
130   int n = W.rows();
131   int n_states = W.columns();
132   const double * const* W_data = W.data_array();
133   int max_d = pair_cost.size()-1;
134 
135    // On completion b(i,j) shows the best prior state (ie at i)
136    // leading to state j at time i+1
137   links_.resize(n-1,n_states);
138   int ** b_data = links_.get_rows()-1;
139      // So that b_data[i] corresponds to i-th row
140 
141     // ci(j) is total cost to get to current state j
142   running_cost_ = W.get_row(0);
143   double *ci = running_cost_.data_block();
144   next_cost_.set_size(n_states);
145   double *ci_new = next_cost_.data_block();
146 
147   for (int i=1;i<n;++i)
148   {
149     int *bi = b_data[i];
150     const double *wi = W_data[i];
151 
152     for (int j=0;j<n_states;++j)
153     {
154       // Evaluate best route to get to state j at time i
155       int k_best = 0;
156       double cost;
157       double wj = wi[j];
158       double cost_best;
159 
160       if (i==1 && first_state>=0)
161       {
162         // Special case: First point pinned down to first_pt
163         k_best = first_state;
164         int d = i0+j-k_best;
165         if (d<0 || d>max_d)
166           cost_best=9e9;
167         else
168           cost_best = ci[k_best] + pair_cost[d]+ wj;
169       }
170       else
171       {
172         // Compute range of k's that can reach this j
173         int klo = std::max(0,j-(max_d-i0));
174         int khi = std::min(n_states-1,j+i0);
175         k_best=klo;
176         cost_best = ci[klo] + pair_cost[i0+j-klo] + wj;
177         for (int k=klo+1;k<=khi;++k)
178         {
179           cost = ci[k] + pair_cost[i0+j-k] + wj;
180           if (cost<cost_best)
181           {
182             cost_best=cost;
183             k_best = k;
184           }
185         }
186       }
187 
188       ci_new[j] = cost_best;
189       bi[j] = k_best;
190     }
191 
192     running_cost_=next_cost_;
193   }
194 }
195 
196 
197 //=======================================================================
198 //: Solve the dynamic programming problem with costs W
199 //  Pair cost term:  C_i(x1,x2) = c(|x1-x2|)
200 //  Size of c indicates maximum displacement between neighbouring
201 //  states.
202 //  If first_state>=0 then the first is constrained to that index value
203 // \retval x  Optimal path
204 // \return Total cost of given path
solve(std::vector<int> & x,const vnl_matrix<double> & W,const vnl_vector<double> & pair_cost,int first_state)205 double mbl_dyn_prog::solve(std::vector<int>& x,
206                            const vnl_matrix<double>& W,
207                            const vnl_vector<double>& pair_cost,
208                            int first_state)
209 {
210   running_costs(W,pair_cost,first_state);
211 
212   double *ci = running_cost_.data_block();
213   int n_states = W.columns();
214 
215   // Find the best final cost
216   int best_j = 0;
217   double best_cost = ci[0];
218   for (int j=1;j<n_states;++j)
219   {
220     if (ci[j]<best_cost) { best_j=j; best_cost=ci[j]; }
221   }
222 
223   construct_path(x,best_j);
224 
225   return best_cost;
226 }
227 
228 //: Solve the dynamic programming problem with costs W
229 //  Pair cost term:  C_i(x1,x2) = pair_cost(i0+x2-x1)
230 //  Size of pair_cost indicates maximum displacement between neighbouring
231 //  states.
232 //  If first_state>=0 then the first is constrained to that index value
233 // \retval x  Optimal path
234 // \return Total cost of given path
solve_asym(std::vector<int> & x,const vnl_matrix<double> & W,const vnl_vector<double> & pair_cost,int i0,int first_state,int last_state)235 double mbl_dyn_prog::solve_asym(std::vector<int>& x,
236                const vnl_matrix<double>& W,
237                const vnl_vector<double>& pair_cost, int i0,
238                int first_state, int last_state)
239 {
240   running_costs_asym(W,pair_cost,i0,first_state);
241 
242   double *ci = running_cost_.data_block();
243   int n_states = W.columns();
244 
245   // Find the best final cost
246   int best_j = 0;
247   double best_cost;
248 
249   if (last_state>=0)
250   {
251     best_j=last_state;
252     best_cost=ci[last_state];
253   }
254   else
255   {
256     best_cost = ci[0];
257     for (int j=1;j<n_states;++j)
258     {
259       if (ci[j]<best_cost) { best_j=j; best_cost=ci[j]; }
260     }
261   }
262   construct_path(x,best_j);
263 
264   return best_cost;
265 }
266 
267 
268 //: Solve the DP problem including constraint between first and last
269 //  Cost of moving from state i to state j is move_cost[j-i]
270 //  (move_cost[i] must be valid for i in range [1-n_states,n_states-1])
271 // Includes cost between x[0] and x[n-1] to ensure loop closure.
272 // \retval x  Optimal path
273 // \return Total cost of given path
solve_loop(std::vector<int> & x,const vnl_matrix<double> & W,const vnl_vector<double> & pair_cost)274 double mbl_dyn_prog::solve_loop(std::vector<int>& x,
275                                 const vnl_matrix<double>& W,
276                                 const vnl_vector<double>& pair_cost)
277 {
278   int n_states = W.columns();
279   int max_d = pair_cost.size()-1;
280 
281   double best_overall_cost=9.9e9;
282 
283   std::vector<int> x1;
284   for (int i0=0;i0<n_states;++i0)
285   {
286     // Solve with constraint that first is i0
287     running_costs(W,pair_cost,i0);
288 
289     double *ci = running_cost_.data_block();
290     // Find the best final cost
291     int klo = std::max(0,i0-max_d);
292     int khi = std::min(n_states-1,i0+max_d);
293     int k_best=klo;
294     double best_cost = ci[klo] + pair_cost[mbl_abs(i0-klo)];
295     for (int k=klo+1;k<=khi;++k)
296     {
297       double cost = ci[k] + pair_cost[mbl_abs(i0-k)];
298       if (cost<best_cost) { best_cost=cost; k_best = k; }
299     }
300 
301     if (best_cost<best_overall_cost)
302     {
303       best_overall_cost=best_cost;
304       construct_path(x,k_best);
305     }
306   }
307 
308   return best_overall_cost;
309 }
310 
311 //=======================================================================
312 // Method: version_no
313 //=======================================================================
314 
version_no() const315 short mbl_dyn_prog::version_no() const
316 {
317   return 1;
318 }
319 
320 //=======================================================================
321 // Method: is_a
322 //=======================================================================
323 
is_a() const324 std::string mbl_dyn_prog::is_a() const
325 {
326   return std::string("mbl_dyn_prog");
327 }
328 
329 //=======================================================================
330 // Method: print_summary
331 //=======================================================================
332 
333 // required if data is present in this class
print_summary(std::ostream & os) const334 void mbl_dyn_prog::print_summary(std::ostream& os) const
335 {
336   os << is_a();
337 }
338 
339 //=======================================================================
340 // Method: save
341 //=======================================================================
342 
343   // required if data is present in this class
b_write(vsl_b_ostream & bfs) const344 void mbl_dyn_prog::b_write(vsl_b_ostream& bfs) const
345 {
346   vsl_b_write(bfs,is_a());
347   vsl_b_write(bfs,version_no());
348 }
349 
350 //=======================================================================
351 // Method: load
352 //=======================================================================
353 
354   // required if data is present in this class
b_read(vsl_b_istream & bfs)355 void mbl_dyn_prog::b_read(vsl_b_istream& bfs)
356 {
357   if (!bfs) return;
358 
359   std::string name;
360   vsl_b_read(bfs,name);
361   if (name != is_a())
362   {
363     std::cerr << "DerivedClass::load :"
364              << " Attempted to load object of type "
365              << name <<" into object of type " << is_a() << std::endl;
366     std::abort();
367   }
368 
369   short version;
370   vsl_b_read(bfs,version);
371   switch (version)
372   {
373     case (1):
374       // vsl_b_read(bfs,data_); // example of data input
375       break;
376     default:
377       std::cerr << "I/O ERROR: vsl_b_read(vsl_b_istream&, mbl_dyn_prog &)\n"
378                << "           Unknown version number "<< version << std::endl;
379       bfs.is().clear(std::ios::badbit); // Set an unrecoverable IO error on stream
380       return;
381   }
382 }
383 
384 //=======================================================================
385 // Associated function: operator<<
386 //=======================================================================
387 
vsl_b_write(vsl_b_ostream & bfs,const mbl_dyn_prog & b)388 void vsl_b_write(vsl_b_ostream& bfs, const mbl_dyn_prog& b)
389 {
390     b.b_write(bfs);
391 }
392 
393 //=======================================================================
394 // Associated function: operator>>
395 //=======================================================================
396 
vsl_b_read(vsl_b_istream & bfs,mbl_dyn_prog & b)397 void vsl_b_read(vsl_b_istream& bfs, mbl_dyn_prog& b)
398 {
399     b.b_read(bfs);
400 }
401 
402 //=======================================================================
403 // Associated function: operator<<
404 //=======================================================================
405 
operator <<(std::ostream & os,const mbl_dyn_prog & b)406 std::ostream& operator<<(std::ostream& os,const mbl_dyn_prog& b)
407 {
408   os << b.is_a() << ": ";
409   vsl_indent_inc(os);
410   b.print_summary(os);
411   vsl_indent_dec(os);
412   return os;
413 }
414