OpenGM  2.3.x
Discrete Graphical Model Library
transportationsolver.hxx
Go to the documentation of this file.
1 #ifndef PRIMALSOLVER_H_
2 #define PRIMALSOLVER_H_
3 #include <iostream>
4 #if (defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64) || defined(__WINDOWS__) || defined(MINGW)) && !defined(CYGWIN)
5 #undef MAXSIZE_T
6 #endif
7 #include <numeric>
8 #include <utility>
9 #include <queue>
10 #include <algorithm>
11 #include <functional>
12 #include <stdexcept>
13 #include <iomanip>
14 #include <cassert>
15 #include <cmath>
16 #include <list>
17 #include <limits>
18 
19 //#define TRWS_DEBUG_OUTPUT
20 
21 #ifdef TRWS_DEBUG_OUTPUT
23 #endif
24 
25 namespace TransportSolver
26 {
27 
28 #ifdef TRWS_DEBUG_OUTPUT
29 using OUT::operator <<;
30 #endif
31 
32 /* List 2D class and implementation ==================================================== */
33 
34 template<class T>
35 class List2D
36 {
37 public:
38 
39  struct bufferElement;
40 
41  struct listElement
42  {
43  listElement(size_t coordinate,bufferElement* pbufElement):
44  _coordinate(coordinate), _pbufElement(pbufElement)
45  {};
46 
47  size_t _coordinate;
49  };
50 
51  typedef std::list<listElement> List1D;
52 
53  template<class Parent,class typeT>
54  class iterator_template : public Parent
55  {
56  public:
57  iterator_template(Parent it,const bufferElement* pbuffer0):Parent(it),_pbuffer0(pbuffer0){};
58  typeT& operator * ()const{return this->Parent::operator *()._pbufElement->_value;}
59 
60  size_t index()const
61  {
62  return (this->Parent::operator *()._pbufElement)-_pbuffer0;
63  }
64 
65  size_t coordinate()const{return this->Parent::operator *()._coordinate;}
66  size_t x()const{return (*this->Parent::operator *()._pbufElement->_rowIterator)._coordinate;}
67  size_t y()const{return (*this->Parent::operator *()._pbufElement->_colIterator)._coordinate;}
68 
69  bool isRowIterator()const{return &(*(this->Parent::operator *()._pbufElement->_rowIterator)) == &(*Parent(*this));}
70 
72  if (isRowIterator())
73  return iterator_template(this->Parent::operator *()._pbufElement->_colIterator,_pbuffer0);
74  else
75  return iterator_template(this->Parent::operator *()._pbufElement->_rowIterator,_pbuffer0);
76  }
77 
78  iterator_template operator ++ (int){iterator_template it=*this; ++(*this); return it;}
81  private:
82  const bufferElement* _pbuffer0;
83  };
84 
85  typedef iterator_template<typename List1D::iterator,T> iterator;
86  typedef iterator_template<typename List1D::const_iterator,const T> const_iterator;
87 
88  typedef std::vector<List1D> List1DSeq;
89 
91  {
92  bufferElement(const T& val,typename List1D::iterator rowIterator,typename List1D::iterator colIterator)
93  :_value(val) {
94  if (_value != NaN()) {
95  _rowIterator = rowIterator;
96  _colIterator = colIterator;
97  }
98  };
99 
101  : _value(other._value)
102  {
103  if (_value != NaN()) {
104  _rowIterator = other._rowIterator;
105  _colIterator = other._colIterator;
106  }
107  }
108 
110  _value = other._value;
111  if (_value != NaN()) {
112  _rowIterator = other._rowIterator;
113  _colIterator = other._colIterator;
114  }
115  return *this;
116  }
117 
119  typename List1D::iterator _rowIterator;
120  typename List1D::iterator _colIterator;
121  };
122 
123  typedef std::vector<bufferElement> Buffer;
124 
125  List2D(size_t xsize, size_t ysize, size_t nnz);
126  List2D(const List2D&);
127  List2D& operator = (const List2D&);
128 
129  void clear();
130  /*
131  * after resizing all data is lost!
132  */
133  void resize(size_t xsize, size_t ysize, size_t nnz);
134 
135  /* tries to insert to the end of lists.
136  * compares coordinates of the last element for this purpose
137  * it it does not work, returns <false>
138  */
139  bool push(size_t x, size_t y, const T& val);
140 
141  /*
142  * tries to insert to the position of the last
143  * call of the function erase(). If it was not called yet -
144  * the last position in the allocated memory.
145  * If the insertion position is occupied,
146  * returns <false>
147  */
148  bool insert(size_t x, size_t y, const T& val);
149  void erase(iterator it);
150  /*
151  * index - index in the _buffer array
152  */
153  void erase(size_t index){erase(iterator(_buffer[index]._rowIterator,&_buffer[0]));}
154 
155  void rowErase(size_t y);
156  void colErase(size_t x);
157 
158  size_t rowSize(size_t y)const{return _rowLists[y].size();};
159  size_t xsize()const{return _colLists.size();}
160  size_t colSize(size_t x)const{return _colLists[x].size();};
161  size_t ysize()const{return _rowLists.size();}
162  size_t nnz()const{return _buffer.size();}
163 
164  iterator rowBegin(size_t y){return iterator(_rowLists[y].begin(),&_buffer[0]);}
165  const_iterator rowBegin(size_t y)const{return const_iterator(_rowLists[y].begin(),&_buffer[0]);}
166 
167  iterator rowEnd(size_t y){return iterator(_rowLists[y].end(),&_buffer[0]);}
168  const_iterator rowEnd(size_t y)const{return const_iterator(_rowLists[y].end(),&_buffer[0]);}
169 
170  iterator colBegin(size_t x){return iterator(_colLists[x].begin(),&_buffer[0]);}
171  const_iterator colBegin(size_t x)const{return const_iterator(_colLists[x].begin(),&_buffer[0]);}
172 
173  iterator colEnd(size_t x){return iterator(_colLists[x].end(),&_buffer[0]);}
174  const_iterator colEnd(size_t x)const{return const_iterator(_colLists[x].end(),&_buffer[0]);}
175 
176  //iterator switchDirection(iterator it)const;
177  template<class BinaryTable1D>
178  T inner_product1D(const BinaryTable1D& bin)const;
179 
180  //pprecision - if non-zero contains an upper bound for the numerical precision of the returned value
181  template<class BinaryTable2D>
182  T inner_product2D(const BinaryTable2D& bin, T* pprecision=0)const;
183 
184  template<class BinaryTable2D>
185  void get2DTable(BinaryTable2D* pbin)const;
186 
187  T& buffer(size_t index){return _buffer[index]._value;}
188  const T& buffer(size_t index)const{return _buffer[index]._value;}
189 
190  std::pair<bool,T> getValue(size_t x,size_t y)const;
191 
192 #ifdef TRWS_DEBUG_OUTPUT
193  void PrintTestData(std::ostream& fout)const;
194 #endif
195 private:
196  bool _insert(size_t x, size_t y, const T& val, size_t position);
197  void _copy(const List2D<T>& lst);
198  static T NaN(){return std::numeric_limits<T>::max();}
199 
200  //size_t _nnz;
201  size_t _insertPosition;
202  size_t _pushPosition;
203  List1DSeq _rowLists;
204  List1DSeq _colLists;
205  Buffer _buffer;
206 };
207 
208 template<class T>
209 List2D<T>::List2D(size_t xsize, size_t ysize, size_t nnz):
210 _insertPosition(nnz-1),
211 _pushPosition(0),
212 _rowLists(ysize),
213 _colLists(xsize),
214 _buffer(nnz,bufferElement(NaN(),typename List1D::iterator(),typename List1D::iterator()))
215 {};
216 
217 template<class T>
219 {
220  _copy(lst);
221 }
222 
223 template<class T>
224 void List2D<T>::resize(size_t xsize, size_t ysize, size_t nnz)
225 {
226  _rowLists.assign(ysize,List1D());
227  _colLists.assign(xsize,List1D());
228  _buffer.assign(nnz,bufferElement(NaN(),typename List1D::iterator(),typename List1D::iterator()));
229  _insertPosition=nnz-1;
230  _pushPosition=0;
231 };
232 
233 
234 template<class T>
235 void List2D<T>::_copy(const List2D<T>& lst)
236 {
237  _buffer=lst._buffer;
238 
239  _rowLists=lst._rowLists;
240  typename List1DSeq::iterator itbeg=_rowLists.begin(), itend=_rowLists.end();
241  for (;itbeg!=itend;++itbeg)
242  {
243  typename List1D::iterator beg=(*itbeg).begin(),end=(*itbeg).end();
244  for (;beg!=end;++beg)
245  {
246  size_t offset=(*beg)._pbufElement-&(lst._buffer[0]);
247  (*beg)._pbufElement= &_buffer[offset];
248  (*beg)._pbufElement->_rowIterator=beg;
249  }
250  }
251 
252  _colLists=lst._colLists;
253  itbeg=_colLists.begin(), itend=_colLists.end();
254  for (;itbeg!=itend;++itbeg)
255  {
256  typename List1D::iterator beg=(*itbeg).begin(),end=(*itbeg).end();
257  for (;beg!=end;++beg)
258  {
259  size_t offset=(*beg)._pbufElement-&(lst._buffer[0]);
260  (*beg)._pbufElement= &_buffer[offset];
261  (*beg)._pbufElement->_colIterator=beg;
262  }
263  }
264 
265  //_nnz=lst._nnz;
266  _insertPosition=lst._insertPosition;
267  _pushPosition=lst._pushPosition;
268 };
269 
270 template<class T>
272 {
273  if (this==&lst)
274  return *this;
275 
276  _copy(lst);
277 
278  return *this;
279 }
280 
281 template<class T>
282 bool List2D<T>::insert(size_t x,size_t y,const T& val)
283 {
284  if (_insert(x,y,val,_insertPosition))
285  {
286  _insertPosition=_buffer.size();
287  return true;
288  }
289 
290  return false;
291 };
292 
293 template<class T>
294 bool List2D<T>::push(size_t x, size_t y, const T& val)
295 {
296  if (_insert(x,y,val,_pushPosition))
297  {
298  ++_pushPosition;
299  //the very last position in _buffer can not be occupied de to push(), only due to insert()
300  if (_pushPosition == (_buffer.size()-1))
301  ++_pushPosition;
302  return true;
303  }
304 
305  return false;
306 }
307 
308 template<class E>
310 {
311 public:
312  coordLess(size_t x):_x(x){}
313  bool operator () (const E& e) const{return e._coordinate < _x;}
314 private:
315  size_t _x;
316 };
317 
318 template<class E>
320 {
321 public:
322  coordMore(size_t x):_x(x){}
323  bool operator () (const E& e) const{return e._coordinate > _x;}
324 private:
325  size_t _x;
326 };
327 
328 template<class T>
329 bool List2D<T>::_insert(size_t x, size_t y, const T& val, size_t position)
330 {
331  assert(x<_colLists.size());
332  assert(y< _rowLists.size());
333 
334  if (position >= _buffer.size())
335  return false;
336 
337  bufferElement& buf=_buffer[position];
338  buf._value=val;
339 
340  List1D& rowList=_rowLists[y];
341  List1D& colList=_colLists[x];
342 
343  typename List1D::iterator insertPosition=std::find_if(rowList.begin(),rowList.end(),coordMore<listElement>(x));
344  buf._rowIterator=rowList.insert(insertPosition,listElement(x,&buf));
345  insertPosition=std::find_if(colList.begin(),colList.end(),coordMore<listElement>(y));
346  buf._colIterator=colList.insert(insertPosition,listElement(y,&buf));
347 
348  return true;
349 };
350 
351 template<class T>
353 {
354  _insertPosition=it.index();
355  size_t x=it.x(), y=it.y();
356  _rowLists[y].erase(_buffer[_insertPosition]._rowIterator);
357  _colLists[x].erase(_buffer[_insertPosition]._colIterator);
358  _buffer[_insertPosition]._value=NaN();
359 };
360 
361 template<class T>
362 void List2D<T>::rowErase(size_t y)
363 {
364  while (!_rowLists[y].empty())
365  erase(iterator(_rowLists[y].begin(),&_buffer[0]));
366 };
367 
368 template<class T>
369 void List2D<T>::colErase(size_t x)
370 {
371  while (!_colLists[x].empty())
372  erase(iterator(_colLists[x].begin(),&_buffer[0]));
373 };
374 
375 template<class T>
377 {
378  for (size_t x=0;x<_rowLists.size();++x)
379  rowErase(x);
380 
381  for (size_t y=0;y<_colLists.size();++y)
382  colErase(y);
383 
384  _pushPosition=0;
385  _insertPosition=_buffer.size()-1;
386 };
387 
388 template<class T>
389 template<class BinaryTable1D>
390 T List2D<T>::inner_product1D(const BinaryTable1D& bin)const
391 {
392  T sum=0;
393  for (size_t i=0; i<_colLists.size();++i)
394  {
395  typename List1D::const_iterator beg=_colLists[i].begin(), end=_colLists[i].end();
396  for (;beg!=end;++beg)
397  sum+=(*beg)._pbufElement->_value * bin[xsize()*((*beg)._coordinate)+i];
398  };
399  return sum;
400 };
401 
402 template<class T>
403 template<class BinaryTable2D>
404 T List2D<T>::inner_product2D(const BinaryTable2D& bin, T* pprecision)const //DEBUG
405 {
406  T floatTypeEps=std::numeric_limits<T>::epsilon();
407  T precision_;
408  T* pprecision_;
409  if (pprecision!=0)
410  pprecision_=pprecision;
411  else
412  pprecision_=&precision_;
413 
414  *pprecision_=0;
415 
416  T sum=0;
417  for (size_t i=0; i<xsize();++i)
418  {
419  const_iterator beg=colBegin(i), end=colEnd(i);
420  for (;beg!=end;++beg)
421  {
422  sum+=(*beg) * bin(beg.x(),beg.y());
423  *pprecision_+=floatTypeEps*fabs(sum);
424  }
425  };
426  return sum;
427 };
428 
429 template<class T>
430 std::pair<bool,T> List2D<T>::getValue(size_t x,size_t y)const
431 {
432  typename List1D::const_iterator beg=_colLists[x].begin(), end=_colLists[x].end();
433  for (;beg!=end;++beg)
434  if ((*beg)._coordinate==y)
435  return std::make_pair(true,(*beg)._pbufElement->_value);
436 
437  return std::make_pair(false,(T)0);
438 };
439 
440 #ifdef TRWS_DEBUG_OUTPUT
441 template<class T>
442 void List2D<T>::PrintTestData(std::ostream& fout)const
443 {
444  fout << "_nnz=" <<_buffer.size()<<std::endl;
445  fout << "_insertPosition=" << _insertPosition<<std::endl;
446  fout << "_pushPosition=" << _pushPosition<<std::endl;
447  fout << "xsize="<<_colLists.size()<<std::endl;
448  fout << "ysize="<<_rowLists.size()<<std::endl;
449 
450  std::vector<T> printBuffer(_buffer.size(),NaN());
451 
452  fout << "row Lists: "<<std::endl;
453  for (size_t i=0; i< _rowLists.size();++i)
454  {
455  fout << "y="<<i<<": ";
456  typename List1D::const_iterator beg=_rowLists[i].begin(), end=_rowLists[i].end();
457  for (;beg!=end;++beg)
458  {
459  fout <<"("<<(*beg)._coordinate<<","<<(*beg)._pbufElement->_value<<")";
460  printBuffer[(*beg)._pbufElement-&_buffer[0]]=(*beg)._pbufElement->_value;
461  }
462  fout <<std::endl;
463  }
464 
465  fout << "column Lists: "<<std::endl;
466  for (size_t i=0; i< _colLists.size();++i)
467  {
468  fout << "x="<<i<<": ";
469  typename List1D::const_iterator beg=_colLists[i].begin(), end=_colLists[i].end();
470  for (;beg!=end;++beg)
471  {
472  fout <<"("<<(*beg)._coordinate<<","<<(*beg)._pbufElement->_value<<")";
473  }
474  fout <<std::endl;
475  }
476 
477  fout << "buffer: ";
478  for (size_t i=0;i<printBuffer.size();++i)
479  if (printBuffer[i]!=NaN())
480  fout << "("<<_buffer[i]._value<<","<<(*_buffer[i]._rowIterator)._coordinate <<","<< (*_buffer[i]._colIterator)._coordinate<<")";
481  else
482  fout << "(nan,nan,nan)";
483  fout << std::endl;
484 
485 };
486 #endif
487 
488 template<class T>
489 template<class BinaryTable2D>
490 void List2D<T>::get2DTable(BinaryTable2D* pbin)const
491 {
492  for (size_t x=0;x<xsize();++x)
493  for (size_t y=0;y<ysize();++y)
494  (*pbin)(x,y)=0;
495 
496  for (size_t i=0; i<xsize();++i)
497  {
498  const_iterator beg=colBegin(i), end=colEnd(i);
499  for (;beg!=end;++beg)
500  (*pbin)(beg.x(),beg.y())=(*beg);
501  };
502 };
503 
504 //=====================================================================================
505 
506 /*
507  * simple matrix class
508  */
509 template<class T>
511 {
512 public:
513  typedef typename std::vector<T>::const_iterator const_iterator;
514  typedef typename std::vector<T>::iterator iterator;
515  typedef T ValueType;
516  MatrixWrapper():_xsize(0),_ysize(0){};
517  MatrixWrapper(size_t xsize,size_t ysize):_xsize(xsize),_ysize(ysize),_array(xsize*ysize){};
518  MatrixWrapper(size_t xsize,size_t ysize, T value):_xsize(xsize),_ysize(ysize),_array(xsize*ysize,value){};
519  void resize(size_t xsize,size_t ysize){_xsize=xsize;_ysize=ysize;_array.resize(xsize*ysize);}; //<! array entries will not be copied!
520  void assign(size_t xsize,size_t ysize,T value){_xsize=xsize;_ysize=ysize;_array.assign(xsize*ysize,value);};
521  const_iterator begin()const {return _array.begin();}
522  const_iterator end ()const {return _array.end();}
523  iterator begin() {return _array.begin();}
524  iterator end () {return _array.end();}
525 
526  const T& operator() (size_t x,size_t y)const{return _array[y*_xsize + x];}
527  T& operator() (size_t x,size_t y) {return _array[y*_xsize + x];}
528  size_t xsize()const{return _xsize;}
529  size_t ysize()const{return _ysize;}
530 
531 #ifdef TRWS_DEBUG_OUTPUT
532  std::ostream& print(std::ostream& out)const
533  {
534  const_iterator it=begin();
535  out<<"["<<_xsize<<","<<_ysize<<"](";
536  for (size_t y=0;y<_ysize;++y)
537  {
538  if (y!=0) out << ",";
539  out <<"(";
540  for (size_t x=0;x<_xsize;++x)
541  {
542  if (x!=0) out << ",";
543  out << *it;
544  ++it;
545  }
546  out << ")";
547  }
548 
549  return out<<")";
550  }
551 #endif
552 
553 private:
554  size_t _xsize, _ysize;
555  std::vector<T> _array;
556 };
557 
558 template<class T>
559 void transpose(const MatrixWrapper<T>& input, MatrixWrapper<T>& result)
560 {
561  result.resize(input.xsize(),input.ysize());
562  for (size_t x=0;x<input.xsize();++x)
563  for (size_t y=0;y<input.ysize();++y)
564  result(y,x)=input(x,y);
565 }
566 
567 /*
568  * Additionally to the functionality provided by std::copy_if it returns indexes of elements satisfying pred
569  */
570 template <class InputIterator, class OutputIteratorValue,class OutputIteratorIndex, class UnaryPredicate>
571 OutputIteratorValue copy_if (InputIterator first, InputIterator last,
572  OutputIteratorValue result, OutputIteratorIndex resultIndex, UnaryPredicate pred)
573 {
574  size_t indx=0;
575  while (first!=last) {
576  if (pred(*first)) {
577  *result = *first;
578  *resultIndex=indx;
579  ++resultIndex;
580  ++result;
581  }
582  ++indx;
583  ++first;
584  }
585  return result;
586 }
587 
588 template<class Iterator,class T>
589 T _Normalize(Iterator begin,Iterator end,T initialValue)
590 {
591  T acc=std::accumulate(begin,end,(T)0.0);
592  std::transform(begin,end,begin,std::bind1st(std::multiplies<T>(),1.0/acc));
593  return initialValue+acc;
594 };
595 
596 //===== TransportationSolver class ==============================================
597 /*
598 * in class OPTIMIZER the member bool bop(const T& a, const T& b) has to be defined. If minimization is meant, then bop== operator <()
599 * if maximization -> bop == operator >()
600 * OPTIMIZER == ACC in opengm notation
601 *
602 * DenseMatrix represents a dense matrix type and has to
603 * - contain elements of the type floatType, defined in common.h
604 * and provide floatType operator ()(size_t index_a, size_t index_b) to access its elements
605 * Examples for Matrix:
606 * MatrixWrapper defined in simpleobjects.h
607 * boost::numeric::ublas::matrix<DD::floatType>;
608 *
609 * see also tests/testcommon.h
610 **
611 **/
612 template<class OPTIMIZER, class DenseMatrix>
614 {
615 public:
616  typedef typename DenseMatrix::ValueType floatType;
617  typedef enum{X, Y} Direction;
618  typedef std::pair<size_t,Direction> CoordDir;
619  typedef std::queue<CoordDir> Queue;
621  typedef std::vector<floatType> UnaryDense;
622  typedef std::vector<size_t> IndexArray;
623  typedef std::list<typename FeasiblePoint::const_iterator> CycleList;
624 
625  static const floatType floatTypeEps;
626  static const size_t defaultMaxIterationNumber;
627  static const size_t MAXSIZE_T;
628 
630 #ifdef TRWS_DEBUG_OUTPUT
631  std::ostream& fout=std::cerr,
632 #endif
633  floatType relativePrecision=floatTypeEps,size_t maxIterationNumber=defaultMaxIterationNumber):
634 #ifdef TRWS_DEBUG_OUTPUT
635  _fout(fout),
636 #endif
637  _pbinInitial(0),_xsize(0),_ysize(0),_relativePrecision(relativePrecision),_basicSolution(0,0,0),_maxIterationNumber(maxIterationNumber)
638  {
639  assert(relativePrecision >0);
640  };
641 
642  TransportationSolver(const size_t& xsize,const size_t& ysize,const DenseMatrix& bin,
643 #ifdef TRWS_DEBUG_OUTPUT
644  std::ostream& fout=std::cout,
645 #endif
646  floatType relativePrecision=floatTypeEps,size_t maxIterationNumber=100):
647 #ifdef TRWS_DEBUG_OUTPUT
648  _fout(fout),
649 #endif
650  _pbinInitial(&bin),_xsize(xsize),_ysize(ysize),_relativePrecision(relativePrecision),_basicSolution(xsize,ysize,_nnz(xsize,ysize)),_maxIterationNumber(maxIterationNumber)
651  {
652  assert(relativePrecision >0);
653  Init(xsize,ysize,bin);
654  };
655 
656 
657  void Init(size_t xsize,size_t ysize,const DenseMatrix& bin);
658  /*
659  * iterators xbegin and ybegin should point out to containers at least xsize and ysize long.
660  * Only the first xsize and ysize will be used.
661  * Iterator should support operation + n, i.e. begin+xsize should be defined
662  * Non-necessary near-zero elements will NOT be considered automatically to avoid numerical problems and save computational time
663  */
664  template <class Iterator>
665  floatType Solve(Iterator xbegin,Iterator ybegin);
666 
667  floatType GetObjectiveValue()const{return _basicSolution.inner_product2D(_matrix, &_primalValueNumericalPrecision);};
668  /*
669  * OutputMatrix should provide operator ()(size_t index_a, size_t index_b) to assign values to its elements
670  */
671  template<class OutputMatrix>
672  floatType GetSolution(OutputMatrix* pbin)const;
673 #ifdef TRWS_DEBUG_OUTPUT
674  void PrintTestData(std::ostream& fout)const;
675  void PrintProblemDescription(const UnaryDense& xarr,const UnaryDense& yarr);
676 #endif
677 private:
678  void _InitBasicSolution(const UnaryDense& xarr,const UnaryDense& yarr);
679  bool _isOptimal(std::pair<size_t,size_t>* pmove);
680  bool _CheckDualConstraints(const UnaryDense& xdual,const UnaryDense& ydual,std::pair<size_t,size_t>* pmove )const;
681  CoordDir _findSingleNeighborNode(const FeasiblePoint&)const;
682  void _BuildDuals(UnaryDense* pxdual,UnaryDense* pydual);
683  void _FindCycle(FeasiblePoint* pfp,const std::pair<size_t,size_t>& move);
684  void _ChangeSolution(const FeasiblePoint& fp,const std::pair<size_t,size_t>& move);
685  bool _MovePotentials(const std::pair<size_t,size_t>& move);
686 
687  void _move2Neighbor(const FeasiblePoint& fp,typename FeasiblePoint::const_iterator &it)const;//helper function - determines, iterator direction and moves it to another element of a list fp with length 2
688 
689  static size_t _nnz(size_t xsize,size_t ysize){return xsize+ysize;}
690  template <class Iterator>
691  static floatType _FilterBound(Iterator xbegin,size_t xsize,UnaryDense& out,IndexArray* pactiveIndexes, floatType precision);
692  void _FilterObjectiveMatrix();
693  void _checkCounter(size_t* pcounter,const char* errmess);
694 
695  mutable floatType _primalValueNumericalPrecision;
696  bool _recalculated;
697 #ifdef TRWS_DEBUG_OUTPUT
698  std::ostream& _fout;
699 #endif
700  const DenseMatrix* _pbinInitial;
701  MatrixWrapper<floatType> _matrix;
702  size_t _xsize,_ysize;
703  floatType _relativePrecision;//relative precision of thresholding input marginal values
704  FeasiblePoint _basicSolution;
705  IndexArray _nonZeroXcoordinates;//_activeXbound;
706  IndexArray _nonZeroYcoordinates;//_activeYbound;
707  size_t _maxIterationNumber;
708 };
709 
710 template<class OPTIMIZER,class DenseMatrix>
711 const typename TransportationSolver<OPTIMIZER,DenseMatrix>::floatType TransportationSolver<OPTIMIZER,DenseMatrix>::floatTypeEps=std::numeric_limits<TransportationSolver<OPTIMIZER,DenseMatrix>::floatType>::epsilon();
712 
713 template<class OPTIMIZER,class DenseMatrix>
714 const size_t TransportationSolver<OPTIMIZER,DenseMatrix>::MAXSIZE_T=std::numeric_limits<size_t>::max();
715 
716 template<class OPTIMIZER,class DenseMatrix>
717 const size_t TransportationSolver<OPTIMIZER,DenseMatrix>::defaultMaxIterationNumber=100;
718 
719 template<class OPTIMIZER,class DenseMatrix>
720 void TransportationSolver<OPTIMIZER,DenseMatrix>::Init(size_t xsize,size_t ysize,const DenseMatrix& bin)
721 {
722  _pbinInitial=&bin;
723  _xsize=xsize;
724  _ysize=ysize;
725  _basicSolution.resize(xsize,ysize,_nnz(xsize,ysize));
726  _nonZeroXcoordinates.clear();
727  _nonZeroYcoordinates.clear();
728  _primalValueNumericalPrecision=0;
729 };
730 
731 template<class OPTIMIZER,class DenseMatrix>
733 _checkCounter (size_t* pcounter, const char* errmess)
734 {
735  if ((*pcounter)++ < std::max(_xsize*_ysize*100,_maxIterationNumber) )//100 - magic number
736  return;
737 
738  throw std::runtime_error(errmess);
739 };
740 
741 template<class OPTIMIZER,class DenseMatrix>
742 void TransportationSolver<OPTIMIZER,DenseMatrix>::
743 _InitBasicSolution(const UnaryDense& xarr,const UnaryDense& yarr)
744 {
745  UnaryDense row=xarr;
746  UnaryDense col=yarr;
747  //north-west corner basic solution
748  //_basicSolution.clear();
749  _basicSolution.resize(xarr.size(),yarr.size(),_nnz(xarr.size(),yarr.size()));
750  typename UnaryDense::iterator rbeg=row.begin(), rend=row.end();
751  typename UnaryDense::iterator cbeg=col.begin(), cend=col.end();
752 
753  size_t counter=0;
754  while ((rbeg!=rend)&&(cbeg!=cend))
755  {
756  if (*cbeg>=*rbeg)
757  {
758  //_basicSolution.push(rbeg.index(), cbeg.index(),*rbeg);
759  _basicSolution.push(rbeg-row.begin(), cbeg-col.begin(),*rbeg);
760  (*cbeg)-=(*rbeg);
761  if (rbeg!=rend)
762  ++rbeg;
763  else
764  ++cbeg;
765  }
766  else
767  {
768  _basicSolution.push(rbeg-row.begin(),cbeg-col.begin(),*cbeg);
769  (*rbeg)-=(*cbeg);
770  if (cbeg!=cend)
771  ++cbeg;
772  else
773  ++rbeg;
774  }
775 
776  _checkCounter(&counter,"_InitBasicSolution-infinite loop!\n");
777  }
778 
779  size_t basicNum=xarr.size()+yarr.size()-1;
780  if (counter!=basicNum)
781  throw std::runtime_error("TransportationSolver::_InitBasicSolution() : INTERNAL ERROR: Can not initialize basic solution!");
782 };
783 
784 
785 /*
786  * returns coordinate + direction of the point which is alone in its row/column.
787  * e.g. (1,X) means that the column with X-coordinate equal to 1, contains a single element.
788  */
789 
790 template<class OPTIMIZER,class DenseMatrix>
791 typename TransportationSolver<OPTIMIZER,DenseMatrix>::CoordDir
792 TransportationSolver<OPTIMIZER,DenseMatrix>::_findSingleNeighborNode(const FeasiblePoint& fp)const
793 {
794  for (size_t i=0;i<_nonZeroXcoordinates.size();++i)
795  if (fp.colSize(i)==1)
796  return std::make_pair(i,X);
797 
798  for (size_t i=0;i<_nonZeroYcoordinates.size();++i)
799  if (fp.rowSize(i)==1)
800  return std::make_pair(i,Y);
801 
802  return std::make_pair(MAXSIZE_T,X);
803 };
804 
805 template<class OPTIMIZER,class DenseMatrix>
806 void TransportationSolver<OPTIMIZER,DenseMatrix>::
807 _BuildDuals(UnaryDense* pxdual,UnaryDense* pydual)
808 {
809  UnaryDense& xdual=*pxdual;
810  UnaryDense& ydual=*pydual;
811 
812  xdual.assign(_nonZeroXcoordinates.size(),0.0);
813  ydual.assign(_nonZeroYcoordinates.size(),0.0);
814 
815  FeasiblePoint fpcopy(_basicSolution);
816  CoordDir currNode=_findSingleNeighborNode(fpcopy);
817 
818  if (currNode.first==MAXSIZE_T)
819  throw std::runtime_error("_BuildDuals: can not build duals: no single neighbor node available!");
820 
821  if (currNode.second==X)
822  {
823  currNode.second=Y;
824  currNode.first=fpcopy.colBegin(currNode.first).coordinate();
825  }else
826  {
827  currNode.second=X;
828  currNode.first=fpcopy.rowBegin(currNode.first).coordinate();
829  }
830 
831  Queue qu;
832  qu.push(currNode);
833 
834  size_t counter=0;
835  do
836  {
837  if (qu.front().second==Y)
838  {
839  size_t y=qu.front().first;
840 
841  typename FeasiblePoint::iterator beg=fpcopy.rowBegin(y),
842  end=fpcopy.rowEnd(y);
843  for (;beg!=end;++beg)
844  {
845  size_t x=beg.coordinate();
846  //xdual[x]=(*_pbin)(x,y)-ydual[y];
847  xdual[x]=_matrix(x,y)-ydual[y];
848  qu.push(std::make_pair(x,X));
849  }
850  fpcopy.rowErase(y);
851 
852  }else
853  {
854  size_t x=qu.front().first;
855 
856  typename FeasiblePoint::iterator beg=fpcopy.colBegin(x),
857  end=fpcopy.colEnd(x);
858  for (;beg!=end;++beg)
859  {
860  size_t y=beg.coordinate();
861  //ydual[y]=(*_pbin)(x,y)-xdual[x];
862  ydual[y]=_matrix(x,y)-xdual[x];
863  qu.push(std::make_pair(y,Y));
864  }
865 
866  fpcopy.colErase(x);
867  }
868 
869  qu.pop();
870 
871  _checkCounter(&counter, "_BuildDuals-infinite loop!\n");
872  }while (!qu.empty());
873 
874 };
875 
876 template<class OPTIMIZER,class DenseMatrix>
877  bool TransportationSolver<OPTIMIZER,DenseMatrix>::
878 _CheckDualConstraints(const UnaryDense& xdual,const UnaryDense& ydual,std::pair<size_t,size_t>* pmove)const
879 {
880  floatType eps=(OPTIMIZER::bop(1,0) ? 1.0 : -1.0)*floatTypeEps;
881  floatType delta, precision;
882 
883  typename MatrixWrapper<floatType>::const_iterator mit=_matrix.begin();
884  for (typename UnaryDense::const_iterator ybeg=ydual.begin();ybeg<ydual.end();++ybeg)
885  for (typename UnaryDense::const_iterator xbeg=xdual.begin();xbeg<xdual.end();++xbeg)
886  {
887  delta=*mit-*xbeg-*ybeg;
888  precision=(fabs(*mit)+fabs(*xbeg)+fabs(*ybeg))*eps;
889  if (OPTIMIZER::bop(delta,precision))
890  {
891  pmove->first=xbeg-xdual.begin(); pmove->second=ybeg-ydual.begin();
892  return false;
893  }
894  ++mit;
895  }
896 
897  return true;
898 };
899 
900 template<class OPTIMIZER,class DenseMatrix>
901 void TransportationSolver<OPTIMIZER,DenseMatrix>::
902 _FindCycle(FeasiblePoint* pfp,const std::pair<size_t,size_t>& move)
903 {
904  FeasiblePoint& fp=*pfp;
905 
906  fp.insert(move.first,move.second,0);
907 
908  CoordDir cd=_findSingleNeighborNode(fp);
909 
910  size_t counter=0;//_initCounter();
911  while (cd.first<MAXSIZE_T)
912  {
913  if (cd.second==X)
914  fp.colErase(cd.first);
915  else
916  fp.rowErase(cd.first);
917 
918  cd=_findSingleNeighborNode(fp);
919 
920  _checkCounter(&counter,"_FindCycle-infinite loop!\n");
921  }
922 };
923 
924 //helper function - determines, iterator direction and moves it to another element of a list fp with length 2
925 template<class OPTIMIZER,class DenseMatrix>
926 void TransportationSolver<OPTIMIZER,DenseMatrix>::
927 _move2Neighbor(const FeasiblePoint& fp,typename FeasiblePoint::const_iterator &it)const
928 {
929  typename FeasiblePoint::const_iterator beg=fp.rowBegin(0);
930  if (it.isRowIterator())
931  {
932  beg=fp.rowBegin(it.y());
933  }
934  else
935  {
936  beg=fp.colBegin(it.x());
937  }
938 
939 if (beg==it)
940  ++it;
941 else
942  --it;
943 };
944 
945 template<class OPTIMIZER,class DenseMatrix>
946 void TransportationSolver<OPTIMIZER,DenseMatrix>::
947 _ChangeSolution(const FeasiblePoint& fp,const std::pair<size_t,size_t>& move)
948 {
949  size_t y=0;
950  for (;y<fp.ysize();++y)
951  {
952  assert( (fp.rowSize(y)==2) || (fp.rowSize(y)==0) ) ;
953  if (fp.rowSize(y)!=0)
954  break;
955  }
956 
957  CycleList plusList, minusList;
958  CycleList* pplus=&plusList, *pPlusList=0;
959  CycleList* pminus=&minusList, *pMinusList=0;
960 
961  //going along the cycle to assign +/- to vertices correctly
962  typename FeasiblePoint::const_iterator it=fp.rowBegin(y);
963  std::pair<size_t,size_t> c0(it.x(),it.y());
964  do{
965  pplus->push_back(it);
966  if ( (it.x()==move.first) && (it.y()==move.second) )
967  {
968  pMinusList=pminus; //really minus list is in *pMinusList
969  pPlusList =pplus;
970  }
971  it=it.changeDir();
972  _move2Neighbor(fp,it);
973  swap(pplus,pminus);
974  }while (! ( (it.x()==c0.first) && (it.y()==c0.second) ) );
975 
976  assert (pMinusList!=0);
977  assert (pPlusList!=0);
978 
979  //selecting the smallest number to add...
980  floatType min=std::numeric_limits<floatType>::max();
981  typename CycleList::const_iterator iterMinVal, beg=pMinusList->begin(), end=pMinusList->end();
982  for (;beg!=end;++beg)
983  {
984  if ( ( **beg < min) ||
985  ( (**beg == min) &&
986  ( (beg->y() < iterMinVal->y()) ||
987  ( ((beg->y() == iterMinVal->y())
988  && (beg->x() < iterMinVal->x())) ) ) ) )
989  {
990  min=**beg;
991  iterMinVal=beg;
992  }
993  }
994 
995  //changing
996  _basicSolution.insert(move.first,move.second,0);
997 
998  beg=pMinusList->begin(), end=pMinusList->end();
999  for (;beg!=end;++beg)
1000  _basicSolution.buffer((*beg).index())-=min;
1001 
1002  beg=pPlusList->begin(), end=pPlusList->end();
1003  for (;beg!=end;++beg)
1004  _basicSolution.buffer((*beg).index())+=min;
1005 
1006  _basicSolution.erase((*iterMinVal).index());
1007 }
1008 
1009 template<class OPTIMIZER,class DenseMatrix>
1010 bool TransportationSolver<OPTIMIZER,DenseMatrix>::
1011 _MovePotentials(const std::pair<size_t,size_t>& move)
1012 {
1013  floatType ObjVal=GetObjectiveValue();
1014  floatType primalValueNumericalPrecisionOld=_primalValueNumericalPrecision;
1015  FeasiblePoint fp=_basicSolution;
1016 
1017  _FindCycle(&fp,move);
1018 
1019  _ChangeSolution(fp,move);
1020 
1021 
1022  floatType newObjValue=GetObjectiveValue();
1023  if ( (OPTIMIZER::bop(ObjVal,newObjValue)) && (fabs(ObjVal-newObjValue)>(_primalValueNumericalPrecision+primalValueNumericalPrecisionOld) ) )
1024  {
1025 #ifdef TRWS_DEBUG_OUTPUT
1026  std::cerr<<_fout<<std::setprecision (std::numeric_limits<floatType>::digits10+1) << std::endl<<"ObjVal="<<ObjVal
1027  <<", newObjValue="<<newObjValue
1028  <<", fabs(ObjVal-newObjValue)="<<fabs(ObjVal-newObjValue)<<", _primalValueNumericalPrecision="<<_primalValueNumericalPrecision
1029  << ", primalValueNumericalPrecisionOld="<< primalValueNumericalPrecisionOld <<std::endl;
1030  _fout << "Basic solution before move:" <<std::endl;
1031  fp.PrintTestData(_fout);
1032  _fout << "Move:" << move<<std::endl;
1033 #endif
1034  return false;
1035  }
1036 
1037  return true;
1038 };
1039 
1040 
1041 template<class OPTIMIZER,class DenseMatrix>
1042 bool TransportationSolver<OPTIMIZER,DenseMatrix>::
1043 _isOptimal(std::pair<size_t,size_t>* pmove)
1044 {
1045  //checks current basic solution for optimality
1046  //1. build duals
1047  UnaryDense xduals,yduals;
1048  _BuildDuals(&xduals,&yduals);
1049  //2. check whether they satisfy dual constraints
1050  return _CheckDualConstraints(xduals,yduals,pmove);
1051 };
1052 
1053 template<class OPTIMIZER,class DenseMatrix>
1054 template <class Iterator>
1055 typename TransportationSolver<OPTIMIZER,DenseMatrix>::floatType TransportationSolver<OPTIMIZER,DenseMatrix>::
1056 _FilterBound(Iterator xbegin,size_t xsize,UnaryDense& out,IndexArray* pactiveIndexes,floatType precision)
1057 {
1058  size_t numOfMeaningfulValues=std::count_if(xbegin,xbegin+xsize,std::bind2nd(std::greater<floatType>(),precision));
1059 
1060  if (numOfMeaningfulValues==0)
1061  throw std::runtime_error("TransportationSolver:_FilterBound(): Error: empty output array. Was the _relativePrecision parameter selected properly?");
1062 
1063  out.resize(numOfMeaningfulValues);
1064  pactiveIndexes->resize(numOfMeaningfulValues);
1065  TransportSolver::copy_if(xbegin,xbegin+xsize,out.begin(),pactiveIndexes->begin(),std::bind2nd(std::greater<floatType>(),precision));
1066  return _Normalize(out.begin(),out.end(),(floatType)0.0);
1067 }
1068 
1069 #ifdef TRWS_DEBUG_OUTPUT
1070 template<class OPTIMIZER,class DenseMatrix>
1071 void TransportationSolver<OPTIMIZER,DenseMatrix>::
1072 PrintProblemDescription(const UnaryDense& xarr,const UnaryDense& yarr)
1073 {
1074  size_t maxprecision=std::numeric_limits<floatType>::digits10;
1075  _fout<< std::setprecision (maxprecision+1) << "xarr=" << xarr<< std::endl;;
1076  _fout<< std::setprecision (maxprecision+1) << "yarr=" << yarr << std::endl;
1077 
1078  for (size_t x=0;x<xarr.size();++x)
1079  for (size_t y=0;y<yarr.size();++y)
1080  _fout << std::setprecision (maxprecision+1)<<"; bin("<<_nonZeroXcoordinates[x]<<","<<_nonZeroYcoordinates[y]<<")="<<_matrix(x,y)<<std::endl;
1081 
1082  _fout <<std::endl<< "Current basic solution:"<<std::endl;
1083  _basicSolution.PrintTestData(_fout);
1084 }
1085 #endif
1086 
1087 template<class OPTIMIZER,class DenseMatrix>
1088 void TransportationSolver<OPTIMIZER,DenseMatrix>::_FilterObjectiveMatrix()
1089 {
1090  _matrix.resize(_nonZeroXcoordinates.size(),_nonZeroYcoordinates.size());
1091  typename MatrixWrapper<floatType>::iterator begin=_matrix.begin();
1092  for (size_t y=0;y<_nonZeroYcoordinates.size();++y)
1093  for (size_t x=0;x<_nonZeroXcoordinates.size();++x)
1094  {
1095  size_t ycurr=_nonZeroYcoordinates[y];
1096  *begin=(*_pbinInitial)(_nonZeroXcoordinates[x],ycurr);
1097  ++begin;
1098  }
1099 }
1100 
1101 template<class OPTIMIZER,class DenseMatrix>
1102 template<class Iterator>
1103 typename TransportationSolver<OPTIMIZER,DenseMatrix>::floatType TransportationSolver<OPTIMIZER,DenseMatrix>::
1104 Solve(Iterator xbegin,Iterator ybegin)
1105 {
1106  _recalculated=false;
1107  UnaryDense xarr,yarr;
1108 
1109  _FilterBound(xbegin,_xsize,xarr,&_nonZeroXcoordinates,_relativePrecision*_xsize*_ysize);
1110  _FilterBound(ybegin,_ysize,yarr,&_nonZeroYcoordinates,_relativePrecision*_xsize*_ysize);
1111  _FilterObjectiveMatrix();
1112 
1113  //1. Create basic solution _basicSolution
1114  _InitBasicSolution(xarr,yarr);
1115 
1116  //2. Check optimality
1117  std::pair<size_t,size_t> move;
1118  bool objectiveImprovementFlag=true;
1119  size_t counter=0;//_initCounter();
1120 
1121  while ((objectiveImprovementFlag)&&(!_isOptimal(&move)))
1122  {
1123  objectiveImprovementFlag=_MovePotentials(move); //changes basic solution
1124  //_checkCounter(&counter,"TransportationSolver::Solve(): maximal number of iterations reached! Try to increase <maxIterationNumber> in constructor.\n");
1125  if (counter++ > std::max(_xsize*_ysize*100,_maxIterationNumber))
1126  {
1127  #ifdef TRWS_DEBUG_OUTPUT
1128  _fout << "Warning! TransportationSolver::Solve(): maximal number of iterations reached! A non-optimal solution is possible!"<<std::endl;
1129  #endif
1130  break;
1131  }
1132  if (!objectiveImprovementFlag)
1133  {
1134 #ifdef TRWS_DEBUG_OUTPUT
1135  PrintProblemDescription(xarr,yarr);
1136 #endif
1137  throw std::runtime_error("TransportationSolver::Solve: INTERNAL ERROR: Objective has become worse. Interrupting!");
1138  }
1139  }
1140  return GetObjectiveValue();
1141 };
1142 
1143 
1144 template<class OPTIMIZER,class DenseMatrix>
1145 template<class OutputMatrix>
1147 {
1148  for (size_t y=0;y<_ysize;++y)
1149  for (size_t x=0;x<_xsize;++x)
1150  (*pbin)(x,y)=0.0;
1151 
1152  MatrixWrapper<floatType> matrix(_basicSolution.xsize(),_basicSolution.ysize());
1153  _basicSolution.get2DTable(&matrix);
1154  for (size_t y=0;y<matrix.ysize();++y)
1155  for (size_t x=0;x<matrix.xsize();++x)
1156  (*pbin)(_nonZeroXcoordinates[x],_nonZeroYcoordinates[y])=matrix(x,y);
1157 
1158  return GetObjectiveValue();
1159 };
1160 
1161 #ifdef TRWS_DEBUG_OUTPUT
1162 template<class OPTIMIZER,class DenseMatrix>
1164 {
1165  fout << "_relativePrecision="<<_relativePrecision<<std::endl;
1166  fout << "_xsize="<<_xsize<<", _ysize="<<_ysize<<std::endl;
1167  fout <<"_basicSolution:"<<std::endl;
1168  _basicSolution.PrintTestData(fout);
1169  fout <<std::endl<< "_nonZeroXcoordinates: "<<_nonZeroXcoordinates;
1170  fout <<std::endl<< "_nonZeroYcoordinates: "<<_nonZeroYcoordinates;
1171 };
1172 #endif
1173 
1174 };//TS
1175 
1176 #endif
std::list< listElement > List1D
iterator_template< typename List1D::const_iterator, const T > const_iterator
void resize(size_t xsize, size_t ysize)
std::list< typename FeasiblePoint::const_iterator > CycleList
void resize(size_t xsize, size_t ysize, size_t nnz)
T inner_product2D(const BinaryTable2D &bin, T *pprecision=0) const
void get2DTable(BinaryTable2D *pbin) const
floatType Solve(Iterator xbegin, Iterator ybegin)
listElement(size_t coordinate, bufferElement *pbufElement)
const_iterator rowEnd(size_t y) const
const_iterator rowBegin(size_t y) const
const_iterator colEnd(size_t x) const
bufferElement & operator=(const bufferElement &other)
MatrixWrapper(size_t xsize, size_t ysize)
const BinaryViewExpression< E1, T1, E2, T2, marray_detail::Times< T1, T2, typename marray_detail::PromoteType< T1, T2 >::type > > operator*(const ViewExpression< E1, T1 > &expression1, const ViewExpression< E2, T2 > &expression2)
Definition: marray.hxx:3251
bool operator()(const E &e) const
void Init(size_t xsize, size_t ysize, const DenseMatrix &bin)
OutputIteratorValue copy_if(InputIterator first, InputIterator last, OutputIteratorValue result, OutputIteratorIndex resultIndex, UnaryPredicate pred)
void assign(size_t xsize, size_t ysize, T value)
std::vector< T >::iterator iterator
bool operator()(const E &e) const
T inner_product1D(const BinaryTable1D &bin) const
List2D(size_t xsize, size_t ysize, size_t nnz)
size_t rowSize(size_t y) const
std::vector< List1D > List1DSeq
void transpose(const MatrixWrapper< T > &input, MatrixWrapper< T > &result)
MatrixWrapper(size_t xsize, size_t ysize, T value)
std::vector< bufferElement > Buffer
floatType GetSolution(OutputMatrix *pbin) const
returns value of the current basic solution
List2D & operator=(const List2D &)
T _Normalize(Iterator begin, Iterator end, T initialValue)
const_iterator colBegin(size_t x) const
iterator_template< typename List1D::iterator, T > iterator
const T & operator()(size_t x, size_t y) const
bufferElement(const T &val, typename List1D::iterator rowIterator, typename List1D::iterator colIterator)
TransportationSolver(const size_t &xsize, const size_t &ysize, const DenseMatrix &bin, floatType relativePrecision=floatTypeEps, size_t maxIterationNumber=100)
iterator_template(Parent it, const bufferElement *pbuffer0)
bool insert(size_t x, size_t y, const T &val)
View< T, false, A > & operator--(View< T, false, A > &v)
Definition: marray.hxx:3120
TransportationSolver(floatType relativePrecision=floatTypeEps, size_t maxIterationNumber=defaultMaxIterationNumber)
size_t colSize(size_t x) const
std::pair< size_t, Direction > CoordDir
bool push(size_t x, size_t y, const T &val)
const T & buffer(size_t index) const
std::vector< T >::const_iterator const_iterator
std::pair< bool, T > getValue(size_t x, size_t y) const
not very efficient function. Implemented mainly for test purposes.
View< T, false, A > & operator++(View< T, false, A > &v)
Definition: marray.hxx:3082