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