Commit 6bc3a489 by Kirill Terekhov

### Maximum product transversal algorithm for dense matrices; function to create permutation matrix

parent b136820a
 ... ... @@ -192,6 +192,13 @@ namespace INMOST if( check_nans_infs((*this)(i,j)) ) return true; return false; } /// Maximum product transversal. /// Computes unsymmetric reordering that maximizes product on diagonal. /// Returns reordering matrix P and scaling matrix S that transforms matrix into I-dominant matrix. /// @param Perm Array for reordering, size of columns of the matrix. /// @param SL Diagonal for rescaling matrix from left, size of columns of the matrix. /// @param SR Diagonal for rescaling matrix from right, size of rows of the matrix. void MPT(INMOST_DATA_ENUM_TYPE * Perm, INMOST_DATA_REAL_TYPE * SL = NULL, INMOST_DATA_REAL_TYPE * SR = NULL) const; /// Singular value decomposition. /// Reconstruct matrix: A = U*Sigma*V.Transpose(). /// Source http://www.public.iastate.edu/~dicook/JSS/paper/code/svd.c ... ... @@ -199,7 +206,8 @@ namespace INMOST /// @param U Left unitary matrix, U^T U = I. /// @param Sigma Diagonal matrix with singular values. /// @param V Right unitary matrix, not transposed. /// @param order_singular_values /// @param order_singular_values Return singular values in descending order. /// @param nonnegative Change sign of singular values. /// \warning Somehow result differ in auto-differential mode. /// \todo Test implementation for auto-differentiation. bool SVD(AbstractMatrix & U, AbstractMatrix & Sigma, AbstractMatrix & V, bool order_singular_values = true, bool nonnegative = true) const ... ... @@ -1339,6 +1347,23 @@ namespace INMOST /// Obtain number of rows. /// @return Reference to number of columns. enumerator & Cols() {return m;} /// Construct row permutation matrix from array of new positions for rows. /// Row permutation matrix multiplies matrix from left. /// Column permutation matrix is obtained by transposition and is multiplied /// from the right. /// Argument Perm is filled with the new position for rows, i.e. /// i-th row takes new position Perm[i] /// @param Perm Array with new positions for rows. /// @param size Size of the array and the resulting matrix. /// @return Permutation matrix. static Matrix Permutation(const INMOST_DATA_ENUM_TYPE * Perm, enumerator size) { Matrix Ret(size,size); Ret.Zero(); for(enumerator k = 0; k < size; ++k) Ret(k,Perm[k]) = 1; return Ret; } /// Convert values in array into square matrix. /// Supports the following representation, depending on the size /// of input array and size of side of final tensors' matrix: ... ... @@ -2380,6 +2405,352 @@ namespace INMOST { return ::INMOST::SubMatrix(*this, first_row, last_row, first_col, last_col); } template struct make_integer; template<> struct make_integer {typedef int type;}; template<> struct make_integer {typedef long long type;}; __INLINE static bool compare(INMOST_DATA_REAL_TYPE * a, INMOST_DATA_REAL_TYPE * b) { return (*reinterpret_cast< make_integer::type * >(a)) <= (*reinterpret_cast< make_integer::type * >(b)); } class BinaryHeapDense { INMOST_DATA_REAL_TYPE * Base; std::vector Array; std::vector Position; public: void Clear() { while(!Array.empty()) { Position[Array.back()-Base] = ENUMUNDEF; Array.pop_back(); } } INMOST_DATA_REAL_TYPE * Get(INMOST_DATA_ENUM_TYPE pos) {return Array[pos];} INMOST_DATA_ENUM_TYPE GetSize() {return static_cast(Array.size());} INMOST_DATA_ENUM_TYPE GetPosition(INMOST_DATA_ENUM_TYPE pos) { return Position[pos]; } INMOST_DATA_ENUM_TYPE DecreaseKey(INMOST_DATA_ENUM_TYPE pos) { INMOST_DATA_ENUM_TYPE i = Position[pos]; ++i; while(i > 1) { //if((*Array[i-1]) <= (*Array[i/2-1])) if( compare(Array[i-1],Array[i/2-1]) ) { Position[(Array[i/2-1]-Base)] = i-1; Position[(Array[i-1]-Base)] = i/2-1; std::swap(Array[i/2-1],Array[i-1]); } else break; i = i/2; } return i; } INMOST_DATA_ENUM_TYPE PushHeap(INMOST_DATA_REAL_TYPE * key) { INMOST_DATA_ENUM_TYPE i = GetSize(); Array.push_back(key); Position[(key-Base)] = i; ++i; while(i > 1) { //if((*Array[i-1]) <= (*Array[i/2-1]) ) if( compare(Array[i-1],Array[i/2-1]) ) { Position[(Array[i-1]-Base)] = i/2-1; Position[(Array[i/2-1]-Base)] = i-1; std::swap(Array[i-1],Array[i/2-1]); } else break; i = i/2; } return i; } void BalanceHeap(INMOST_DATA_ENUM_TYPE i) { INMOST_DATA_ENUM_TYPE Index; ++i; while(i <= Array.size()/2) { if( 2*i+1 > Array.size() ) Index = 2*i; //else if( (*Array[2*i-1]) <= (*Array[2*i+1-1]) ) else if( compare(Array[2*i-1],Array[2*i+1-1]) ) Index = 2*i; else Index = 2*i+1; //if(!((*Array[i-1]) <= (*Array[Index-1]))) if(!compare(Array[i-1],Array[Index-1])) { Position[(Array[i-1]-Base)] = Index-1; Position[(Array[Index-1]-Base)] = i-1; std::swap(Array[i-1],Array[Index-1]); } else break; i = Index; } } INMOST_DATA_ENUM_TYPE PopHeap() { INMOST_DATA_ENUM_TYPE Ret = ENUMUNDEF; if(Array.empty()) return Ret; Ret = static_cast(Array[0]-Base); Array[0] = Array.back(); Position[Array[0] - Base] = 0; Array.pop_back(); Position[Ret] = ENUMUNDEF; BalanceHeap(0); return Ret; } BinaryHeapDense(INMOST_DATA_REAL_TYPE * Base, INMOST_DATA_ENUM_TYPE Size) : Base(Base) { Position.resize(Size,ENUMUNDEF); Array.reserve(4096); } ~BinaryHeapDense() { } }; template void AbstractMatrix::MPT(INMOST_DATA_ENUM_TYPE * Perm, INMOST_DATA_REAL_TYPE * SL, INMOST_DATA_REAL_TYPE * SR) const { const INMOST_DATA_ENUM_TYPE EOL = ENUMUNDEF-1; int n = Rows(); int m = Cols(); INMOST_DATA_REAL_TYPE u, l; array Cmax(m,0.0); array U(m,std::numeric_limits::max()); array V(n,std::numeric_limits::max()); std::fill(Perm,Perm+m,ENUMUNDEF); //array Perm(m,ENUMUNDEF); array IPerm(std::max(n,m),ENUMUNDEF); array ColumnList(m,ENUMUNDEF); array ColumnPosition(n,ENUMUNDEF); array Parent(n,ENUMUNDEF); array Dist(m,std::numeric_limits::max()); const AbstractMatrix & A = *this; Matrix C(n,m); INMOST_DATA_ENUM_TYPE Li, Ui; INMOST_DATA_ENUM_TYPE ColumnBegin, PathEnd, Trace, IPermPrev; INMOST_DATA_REAL_TYPE ShortestPath, AugmentPath; BinaryHeapDense Heap(&Dist[0],m); //Initial LOG transformation to dual problem and initial extreme match for(int k = 0; k < n; ++k) { for(int i = 0; i < m; ++i) { C(k,i) = fabs(get_value(A(k,i))); if( Cmax[i] < C(k,i) ) Cmax[i] = C(k,i); } } for(int k = 0; k < n; ++k) { for (int i = 0; i < m; ++i) { if( Cmax[i] == 0 || C(k,i) == 0 ) C(k,i) = std::numeric_limits::max(); else { C(k,i) = log(Cmax[i])-log(C(k,i)); if( C(k,i) < U[i] ) U[i] = C(k,i); } } } for(int k = 0; k < n; ++k) { for (int i = 0; i < m; ++i) { u = C(k,i) - U[i]; if( u < V[k] ) V[k] = u; } } // Update cost and match for(int k = 0; k < n; ++k) { for (int i = 0; i < m; ++i) { u = fabs(C(k,i) - V[k] - U[i]); if( u < 1.0e-30 && Perm[i] == ENUMUNDEF && IPerm[k] == ENUMUNDEF ) { Perm[i] = k; IPerm[k] = i; ColumnPosition[k] = i; } } } //1-step augmentation for(int k = 0; k < n; ++k) { if( IPerm[k] == ENUMUNDEF ) //unmatched row { for (int i = 0; i < m && IPerm[k] == ENUMUNDEF; ++i) { u = fabs(C(k,i) - V[k] - U[i]); if( u <= 1.0e-30 ) { Li = Perm[i]; assert(Li != ENUMUNDEF); // Search other row in C for 0 for (int Lit = 0; Lit < m; ++Lit) { u = fabs(C(Li,Lit) - V[Li] - U[Lit]); if( u <= 1.0e-30 && Perm[Lit] == ENUMUNDEF ) { Perm[i] = k; IPerm[k] = i; ColumnPosition[k] = i; Perm[Lit] = Li; IPerm[Li] = Lit; ColumnPosition[Li] = Lit; break; } } } } } } // Weighted bipartite matching for(int k = 0; k < n; ++k) { if( IPerm[k] != ENUMUNDEF ) continue; Li = k; ColumnBegin = EOL; Parent[Li] = ENUMUNDEF; PathEnd = ENUMUNDEF; Trace = k; ShortestPath = 0; AugmentPath = std::numeric_limits::max(); while(true) { for (int Lit = 0; Lit < m; ++Lit) { if( ColumnList[Lit] != ENUMUNDEF ) continue; l = ShortestPath + C(Li,Lit) - V[Li] - U[Lit]; if( l < 0.0 && fabs(l) < 1.0e-12 ) l = 0; if( l < AugmentPath ) { if( Perm[Lit] == ENUMUNDEF ) { PathEnd = Lit; Trace = Li; AugmentPath = l; } else if( l < Dist[Lit] ) { Dist[Lit] = l; Parent[Perm[Lit]] = Li; if( Heap.GetPosition(Lit) != ENUMUNDEF ) Heap.DecreaseKey(Lit); else Heap.PushHeap(&Dist[Lit]); } } } INMOST_DATA_ENUM_TYPE pop_heap_pos = Heap.PopHeap(); if( pop_heap_pos == ENUMUNDEF ) break; Ui = pop_heap_pos; ShortestPath = Dist[Ui]; if( AugmentPath <= ShortestPath ) { Dist[Ui] = std::numeric_limits::max(); break; } ColumnList[Ui] = ColumnBegin; ColumnBegin = Ui; Li = Perm[Ui]; } if( PathEnd != ENUMUNDEF ) { Ui = ColumnBegin; while(Ui != EOL) { U[Ui] += Dist[Ui] - AugmentPath; if( Perm[Ui] != ENUMUNDEF ) V[Perm[Ui]] = C(Perm[Ui],ColumnPosition[Perm[Ui]]) - U[Ui]; Dist[Ui] = std::numeric_limits::max(); Li = ColumnList[Ui]; ColumnList[Ui] = ENUMUNDEF; Ui = Li; } Ui = PathEnd; while(Trace != ENUMUNDEF) { IPermPrev = IPerm[Trace]; Perm[Ui] = Trace; IPerm[Trace] = Ui; ColumnPosition[Trace] = Ui; V[Trace] = C(Trace,ColumnPosition[Trace]) - U[Ui]; Ui = IPermPrev; Trace = Parent[Trace]; } for(Ui = 0; Ui < Heap.GetSize(); ++Ui) *Heap.Get(Ui) = std::numeric_limits::max(); Heap.Clear(); } } if( SL || SR ) { for (int k = 0; k < std::min(n,m); ++k) { l = (V[k] == std::numeric_limits::max() ? 1 : exp(V[k])); u = (U[k] == std::numeric_limits::max() || Cmax[k] == 0 ? 1 : exp(U[k])/Cmax[k]); if( l*get_value(A(k,Perm[k]))*u < 0.0 ) l *= -1; if( SL ) SL[Perm[k]] = u; if( SR ) SR[k] = l; } if( SR ) for(int k = std::min(n,m); k < n; ++k) SR[k] = 1; if( SL ) for(int k = std::min(n,m); k < m; ++k) SL[k] = 1; } //check that there are no gaps in Perm std::fill(IPerm.begin(),IPerm.end(),ENUMUNDEF); std::vector gaps; for(int k = 0; k < m; ++k) { if( Perm[k] != ENUMUNDEF ) IPerm[Perm[k]] = 0; } for(int k = 0; k < m; ++k) { if( IPerm[k] == ENUMUNDEF ) gaps.push_back(k); } for(int k = 0; k < m; ++k) { if( Perm[k] == ENUMUNDEF ) { Perm[k] = gaps.back(); gaps.pop_back(); } } } /// shortcut for matrix of integer values. typedef Matrix iMatrix; /// shortcut for matrix of real values. ... ...
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!