Real ComplementRatio ( const ElementalMatrix<Real>& sPre, const ElementalMatrix<Real>& zPre ) { DEBUG_CSE ElementalProxyCtrl ctrl; ctrl.colConstrain = true; ctrl.colAlign = 0; DistMatrixReadProxy<Real,Real,VC,STAR> sProx( sPre, ctrl ), zProx( zPre, ctrl ); auto& s = sProx.GetLocked(); auto& z = zProx.GetLocked(); const Int localHeight = s.LocalHeight(); const Real* sBuf = s.LockedBuffer(); const Real* zBuf = z.LockedBuffer(); Real maxLocProd = 0; for( Int iLoc=0; iLoc<localHeight; ++iLoc ) maxLocProd = Max( sBuf[iLoc]*zBuf[iLoc], maxLocProd ); const Real maxProd = mpi::AllReduce( maxLocProd, mpi::MAX, s.DistComm() ); Real minLocProd = maxProd; for( Int iLoc=0; iLoc<localHeight; ++iLoc ) minLocProd = Min( sBuf[iLoc]*zBuf[iLoc], minLocProd ); const Real minProd = mpi::AllReduce( minLocProd, mpi::MIN, s.DistComm() ); return maxProd/minProd; }
void NesterovTodd ( const ElementalMatrix<Real>& sPre, const ElementalMatrix<Real>& zPre, ElementalMatrix<Real>& wPre ) { DEBUG_CSE ElementalProxyCtrl ctrl; ctrl.colConstrain = true; ctrl.colAlign = 0; DistMatrixReadProxy<Real,Real,VC,STAR> sProx( sPre, ctrl ), zProx( zPre, ctrl ); DistMatrixWriteProxy<Real,Real,VC,STAR> wProx( wPre, ctrl ); auto& s = sProx.GetLocked(); auto& z = zProx.GetLocked(); auto& w = wProx.Get(); w.Resize( s.Height(), 1 ); const Int localHeight = w.LocalHeight(); const Real* sBuf = s.LockedBuffer(); const Real* zBuf = z.LockedBuffer(); Real* wBuf = w.Buffer(); for( Int iLoc=0; iLoc<localHeight; ++iLoc ) wBuf[iLoc] = Sqrt(sBuf[iLoc]/zBuf[iLoc]); }
void Apply ( const ElementalMatrix<Real>& xPre, const ElementalMatrix<Real>& yPre, ElementalMatrix<Real>& zPre, const ElementalMatrix<Int>& ordersPre, const ElementalMatrix<Int>& firstIndsPre, Int cutoff ) { DEBUG_ONLY(CSE cse("soc::Apply")) AssertSameGrids( xPre, yPre, zPre, ordersPre, firstIndsPre ); ElementalProxyCtrl ctrl; ctrl.colConstrain = true; ctrl.colAlign = 0; DistMatrixReadProxy<Real,Real,VC,STAR> xProx( xPre, ctrl ), yProx( yPre, ctrl ); DistMatrixWriteProxy<Real,Real,VC,STAR> zProx( zPre, ctrl ); DistMatrixReadProxy<Int,Int,VC,STAR> ordersProx( ordersPre, ctrl ), firstIndsProx( firstIndsPre, ctrl ); auto& x = xProx.GetLocked(); auto& y = yProx.GetLocked(); auto& z = zProx.Get(); auto& orders = ordersProx.GetLocked(); auto& firstInds = firstIndsProx.GetLocked(); soc::Dots( x, y, z, orders, firstInds ); auto xRoots = x; auto yRoots = y; cone::Broadcast( xRoots, orders, firstInds ); cone::Broadcast( yRoots, orders, firstInds ); const Int localHeight = x.LocalHeight(); const Real* xBuf = x.LockedBuffer(); const Real* xRootBuf = xRoots.LockedBuffer(); const Real* yBuf = y.LockedBuffer(); const Real* yRootBuf = yRoots.LockedBuffer(); Real* zBuf = z.Buffer(); const Int* firstIndBuf = firstInds.LockedBuffer(); for( Int iLoc=0; iLoc<localHeight; ++iLoc ) { const Int i = x.GlobalRow(iLoc); const Int firstInd = firstIndBuf[iLoc]; if( i != firstInd ) zBuf[iLoc] += xRootBuf[iLoc]*yBuf[iLoc] + yRootBuf[iLoc]*xBuf[iLoc]; } }
void PushPairInto ( ElementalMatrix<Real>& sPre, ElementalMatrix<Real>& zPre, const ElementalMatrix<Real>& wPre, const ElementalMatrix<Int>& ordersPre, const ElementalMatrix<Int>& firstIndsPre, Real wMaxNormLimit, Int cutoff ) { DEBUG_ONLY(CSE cse("soc::PushPairInto")) AssertSameGrids( sPre, zPre, wPre, ordersPre, firstIndsPre ); ElementalProxyCtrl ctrl; ctrl.colConstrain = true; ctrl.colAlign = 0; DistMatrixReadWriteProxy<Real,Real,VC,STAR> sProx( sPre, ctrl ), zProx( zPre, ctrl ); DistMatrixReadProxy<Real,Real,VC,STAR> wProx( wPre, ctrl ); DistMatrixReadProxy<Int,Int,VC,STAR> ordersProx( ordersPre, ctrl ), firstIndsProx( firstIndsPre, ctrl ); auto& s = sProx.Get(); auto& z = zProx.Get(); auto& w = wProx.GetLocked(); auto& orders = ordersProx.GetLocked(); auto& firstInds = firstIndsProx.GetLocked(); DistMatrix<Real,VC,STAR> sLower(s.Grid()), zLower(z.Grid()); soc::LowerNorms( s, sLower, orders, firstInds, cutoff ); soc::LowerNorms( z, zLower, orders, firstInds, cutoff ); const Int localHeight = s.LocalHeight(); for( Int iLoc=0; iLoc<localHeight; ++iLoc ) { const Int i = s.GlobalRow(iLoc); const Real w0 = w.GetLocal(iLoc,0); if( i == firstInds.GetLocal(iLoc,0) && w0 > wMaxNormLimit ) { // TODO: Switch to a non-adhoc modification s.UpdateLocal( iLoc, 0, Real(1)/wMaxNormLimit ); z.UpdateLocal( iLoc, 0, Real(1)/wMaxNormLimit ); } } }
Int ADMM ( const AbstractDistMatrix<Real>& APre, const AbstractDistMatrix<Real>& bPre, const AbstractDistMatrix<Real>& cPre, AbstractDistMatrix<Real>& zPre, const ADMMCtrl<Real>& ctrl ) { EL_DEBUG_CSE DistMatrixReadProxy<Real,Real,MC,MR> AProx( APre ), bProx( bPre ), cProx( cPre ); DistMatrixWriteProxy<Real,Real,MC,MR> zProx( zPre ); auto& A = AProx.GetLocked(); auto& b = bProx.GetLocked(); auto& c = cProx.GetLocked(); auto& z = zProx.Get(); // Cache a custom partially-pivoted LU factorization of // | rho*I A^H | = | B11 B12 | // | A 0 | | B21 B22 | // by (justifiably) avoiding pivoting in the first n steps of // the factorization, so that // [I,rho*I] = lu(rho*I). // The factorization would then proceed with // B21 := B21 U11^{-1} = A (rho*I)^{-1} = A/rho // B12 := L11^{-1} B12 = I A^H = A^H. // The Schur complement would then be // B22 := B22 - B21 B12 = 0 - (A*A^H)/rho. // We then factor said matrix with LU with partial pivoting and // swap the necessary rows of B21 in order to implicitly commute // the row pivots with the Gauss transforms in the manner standard // for GEPP. Unless A A' is singular, pivoting should not be needed, // as Cholesky factorization of the negative matrix should be valid. // // The result is the factorization // | I 0 | | rho*I A^H | = | I 0 | | rho*I U12 |, // | 0 P22 | | A 0 | | L21 L22 | | 0 U22 | // where [L22,U22] are stored within B22. const Int m = A.Height(); const Int n = A.Width(); const Grid& grid = A.Grid(); DistMatrix<Real> U12(grid), L21(grid), B22(grid), bPiv(grid); U12.Align( 0, n%U12.RowStride() ); L21.Align( n%L21.ColStride(), 0 ); B22.Align( n%B22.ColStride(), n%B22.RowStride() ); Adjoint( A, U12 ); L21 = A; L21 *= 1/ctrl.rho; Herk( LOWER, NORMAL, -1/ctrl.rho, A, B22 ); MakeHermitian( LOWER, B22 ); DistPermutation P2(grid); LU( B22, P2 ); P2.PermuteRows( L21 ); bPiv = b; P2.PermuteRows( bPiv ); // Possibly form the inverse of L22 U22 DistMatrix<Real> X22(grid); if( ctrl.inv ) { X22 = B22; MakeTrapezoidal( LOWER, X22 ); FillDiagonal( X22, Real(1) ); TriangularInverse( LOWER, UNIT, X22 ); Trsm( LEFT, UPPER, NORMAL, NON_UNIT, Real(1), B22, X22 ); } Int numIter=0; DistMatrix<Real> g(grid), xTmp(grid), y(grid), t(grid); Zeros( g, m+n, 1 ); PartitionDown( g, xTmp, y, n ); DistMatrix<Real> x(grid), u(grid), zOld(grid), xHat(grid); Zeros( z, n, 1 ); Zeros( u, n, 1 ); Zeros( t, n, 1 ); while( numIter < ctrl.maxIter ) { zOld = z; // Find x from // | rho*I A^H | | x | = | rho*(z-u)-c | // | A 0 | | y | | b | // via our cached custom factorization: // // |x| = inv(U) inv(L) P' |rho*(z-u)-c| // |y| |b | // = |rho*I U12|^{-1} |I 0 | |I 0 | |rho*(z-u)-c| // = |0 U22| |L21 L22| |0 P22'| |b | // = " " |rho*(z-u)-c| // | P22' b | xTmp = z; xTmp -= u; xTmp *= ctrl.rho; xTmp -= c; y = bPiv; Gemv( NORMAL, Real(-1), L21, xTmp, Real(1), y ); if( ctrl.inv ) { Gemv( NORMAL, Real(1), X22, y, t ); y = t; } else { Trsv( LOWER, NORMAL, UNIT, B22, y ); Trsv( UPPER, NORMAL, NON_UNIT, B22, y ); } Gemv( NORMAL, Real(-1), U12, y, Real(1), xTmp ); xTmp *= 1/ctrl.rho; // xHat := alpha*x + (1-alpha)*zOld xHat = xTmp; xHat *= ctrl.alpha; Axpy( 1-ctrl.alpha, zOld, xHat ); // z := pos(xHat+u) z = xHat; z += u; LowerClip( z, Real(0) ); // u := u + (xHat-z) u += xHat; u -= z; const Real objective = Dot( c, xTmp ); // rNorm := || x - z ||_2 t = xTmp; t -= z; const Real rNorm = FrobeniusNorm( t ); // sNorm := |rho| || z - zOld ||_2 t = z; t -= zOld; const Real sNorm = Abs(ctrl.rho)*FrobeniusNorm( t ); const Real epsPri = Sqrt(Real(n))*ctrl.absTol + ctrl.relTol*Max(FrobeniusNorm(xTmp),FrobeniusNorm(z)); const Real epsDual = Sqrt(Real(n))*ctrl.absTol + ctrl.relTol*Abs(ctrl.rho)*FrobeniusNorm(u); if( ctrl.print ) { t = xTmp; LowerClip( t, Real(0) ); t -= xTmp; const Real clipDist = FrobeniusNorm( t ); if( grid.Rank() == 0 ) cout << numIter << ": " << "||x-z||_2=" << rNorm << ", " << "epsPri=" << epsPri << ", " << "|rho| ||z-zOld||_2=" << sNorm << ", " << "epsDual=" << epsDual << ", " << "||x-Pos(x)||_2=" << clipDist << ", " << "c'x=" << objective << endl; } if( rNorm < epsPri && sNorm < epsDual ) break; ++numIter; } if( ctrl.maxIter == numIter && grid.Rank() == 0 ) cout << "ADMM failed to converge" << endl; x = xTmp; return numIter; }