inline void NestedDissectionRecursion ( const DistGraph& graph, const DistMap& perm, DistSeparator& sep, DistNodeInfo& node, Int off, const BisectCtrl& ctrl ) { DEBUG_ONLY(CSE cse("ldl::NestedDissectionRecursion")) mpi::Comm comm = graph.Comm(); const int commSize = mpi::Size(comm); mpi::Dup( comm, sep.comm ); mpi::Dup( comm, node.comm ); if( commSize > 1 ) { const Int numLocalSources = graph.NumLocalSources(); const Int firstLocalSource = graph.FirstLocalSource(); const Int* offsetBuf = graph.LockedOffsetBuffer(); const Int* targetBuf = graph.LockedTargetBuffer(); // Partition the graph and construct the inverse map DistGraph child; bool childIsOnLeft; DistMap map; const Int sepSize = Bisect( graph, child, map, childIsOnLeft, ctrl ); const Int numSources = graph.NumSources(); const Int childSize = child.NumSources(); const Int leftChildSize = ( childIsOnLeft ? childSize : numSources-sepSize-childSize ); DistMap invMap; InvertMap( map, invMap ); // Mostly fill this node of the DistSeparatorTree // (we will finish computing the separator indices at the end) sep.off = off + (numSources-sepSize); sep.inds.resize( sepSize ); for( Int s=0; s<sepSize; ++s ) sep.inds[s] = s + (numSources-sepSize); invMap.Translate( sep.inds ); // Fill in this node of the DistNode node.size = sepSize; node.off = sep.off; set<Int> localLowerStruct; for( Int s=0; s<sepSize; ++s ) { const Int source = sep.inds[s]; if( source >= firstLocalSource && source < firstLocalSource+numLocalSources ) { const Int localSource = source - firstLocalSource; const Int edgeOff = offsetBuf[localSource]; const Int numConn = offsetBuf[localSource+1] - edgeOff; for( Int t=0; t<numConn; ++t ) { const Int target = targetBuf[edgeOff+t]; if( target >= numSources ) localLowerStruct.insert( off+target ); } } } const int numLocalConnected = localLowerStruct.size(); vector<int> localConnectedSizes( commSize ); mpi::AllGather ( &numLocalConnected, 1, localConnectedSizes.data(), 1, comm ); vector<Int> localConnectedVec; CopySTL( localLowerStruct, localConnectedVec ); vector<int> localConnectedOffs; const int sumOfLocalConnectedSizes = Scan( localConnectedSizes, localConnectedOffs ); vector<Int> localConnections( sumOfLocalConnectedSizes ); mpi::AllGather ( localConnectedVec.data(), numLocalConnected, localConnections.data(), localConnectedSizes.data(), localConnectedOffs.data(), comm ); set<Int> lowerStruct ( localConnections.begin(), localConnections.end() ); CopySTL( lowerStruct, node.origLowerStruct ); // Finish computing the separator indices perm.Translate( sep.inds ); // Construct map from child indices to the original ordering DistMap newPerm( child.NumSources(), child.Comm() ); const Int localChildSize = child.NumLocalSources(); const Int firstLocalChildSource = child.FirstLocalSource(); auto& newPermLoc = newPerm.Map(); if( childIsOnLeft ) for( Int s=0; s<localChildSize; ++s ) newPermLoc[s] = s+firstLocalChildSource; else for( Int s=0; s<localChildSize; ++s ) newPermLoc[s] = s+firstLocalChildSource+leftChildSize; invMap.Extend( newPerm ); perm.Extend( newPerm ); // Recurse const Int childOff = ( childIsOnLeft ? off : off+leftChildSize ); sep.child = new DistSeparator(&sep); node.child = new DistNodeInfo(&node); node.child->onLeft = childIsOnLeft; NestedDissectionRecursion ( child, newPerm, *sep.child, *node.child, childOff, ctrl ); } else { Graph seqGraph( graph ); sep.duplicate = new Separator(&sep); node.duplicate = new NodeInfo(&node); NestedDissectionRecursion ( seqGraph, perm.Map(), *sep.duplicate, *node.duplicate, off, ctrl ); // Pull information up from the duplicates sep.off = sep.duplicate->off; sep.inds = sep.duplicate->inds; node.size = node.duplicate->size; node.off = node.duplicate->off; node.origLowerStruct = node.duplicate->origLowerStruct; } }
inline void NestedDissectionRecursion ( const Graph& graph, const vector<Int>& perm, Separator& sep, NodeInfo& node, Int off, const BisectCtrl& ctrl ) { DEBUG_CSE const Int numSources = graph.NumSources(); const Int* offsetBuf = graph.LockedOffsetBuffer(); const Int* sourceBuf = graph.LockedSourceBuffer(); const Int* targetBuf = graph.LockedTargetBuffer(); if( numSources <= ctrl.cutoff ) { // Filter out the graph of the diagonal block Int numValidEdges = 0; const Int numEdges = graph.NumEdges(); for( Int e=0; e<numEdges; ++e ) if( targetBuf[e] < numSources ) ++numValidEdges; vector<Int> subOffsets(numSources+1), subTargets(Max(numValidEdges,1)); Int sourceOff = 0; Int validCounter = 0; Int prevSource = -1; for( Int e=0; e<numEdges; ++e ) { const Int source = sourceBuf[e]; const Int target = targetBuf[e]; while( source != prevSource ) { subOffsets[sourceOff++] = validCounter; ++prevSource; } if( target < numSources ) subTargets[validCounter++] = target; } while( sourceOff <= numSources ) { subOffsets[sourceOff++] = validCounter; } // Technically, SuiteSparse expects column-major storage, but since // the matrix is structurally symmetric, it's okay to pass in the // row-major representation vector<Int> amdPerm; AMDOrder( subOffsets, subTargets, amdPerm ); // Compute the symbolic factorization of this leaf node using the // reordering just computed node.LOffsets.resize( numSources+1 ); node.LParents.resize( numSources ); vector<Int> LNnz( numSources ), Flag( numSources ), amdPermInv( numSources ); suite_sparse::ldl::Symbolic ( numSources, subOffsets.data(), subTargets.data(), node.LOffsets.data(), node.LParents.data(), LNnz.data(), Flag.data(), amdPerm.data(), amdPermInv.data() ); // Fill in this node of the local separator tree sep.off = off; sep.inds.resize( numSources ); for( Int i=0; i<numSources; ++i ) sep.inds[i] = perm[amdPerm[i]]; // TODO: Replace with better deletion mechanism SwapClear( sep.children ); // Fill in this node of the local elimination tree node.size = numSources; node.off = off; // TODO: Replace with better deletion mechanism SwapClear( node.children ); set<Int> lowerStruct; for( Int s=0; s<node.size; ++s ) { const Int edgeOff = offsetBuf[s]; const Int numConn = offsetBuf[s+1] - edgeOff; for( Int t=0; t<numConn; ++t ) { const Int target = targetBuf[edgeOff+t]; if( target >= numSources ) lowerStruct.insert( off+target ); } } CopySTL( lowerStruct, node.origLowerStruct ); } else { DEBUG_ONLY( if( !IsSymmetric(graph) ) { Print( graph, "graph" ); LogicError("Graph was not symmetric"); } ) // Partition the graph and construct the inverse map Graph leftChild, rightChild; vector<Int> map; const Int sepSize = Bisect( graph, leftChild, rightChild, map, ctrl ); vector<Int> invMap( numSources ); for( Int s=0; s<numSources; ++s ) invMap[map[s]] = s; DEBUG_ONLY( if( !IsSymmetric(leftChild) ) { Print( graph, "graph" ); Print( leftChild, "leftChild" ); LogicError("Left child was not symmetric"); } )
inline void NaturalNestedDissectionRecursion ( Int nx, Int ny, Int nz, const Graph& graph, const vector<Int>& perm, Separator& sep, NodeInfo& node, Int off, Int cutoff ) { EL_DEBUG_CSE const Int numSources = graph.NumSources(); const Int* offsetBuf = graph.LockedOffsetBuffer(); const Int* sourceBuf = graph.LockedSourceBuffer(); const Int* targetBuf = graph.LockedTargetBuffer(); if( numSources <= cutoff ) { // Filter out the graph of the diagonal block Int numValidEdges = 0; const Int numEdges = graph.NumEdges(); for( Int e=0; e<numEdges; ++e ) if( targetBuf[e] < numSources ) ++numValidEdges; vector<Int> subOffsets(numSources+1), subTargets(Max(numValidEdges,1)); Int sourceOff = 0; Int validCounter = 0; Int prevSource = -1; for( Int e=0; e<numEdges; ++e ) { const Int source = sourceBuf[e]; const Int target = targetBuf[e]; while( source != prevSource ) { subOffsets[sourceOff++] = validCounter; ++prevSource; } if( target < numSources ) subTargets[validCounter++] = target; } while( sourceOff <= numSources) { subOffsets[sourceOff++] = validCounter; } // Technically, SuiteSparse expects column-major storage, but since // the matrix is structurally symmetric, it's okay to pass in the // row-major representation vector<Int> amdPerm; AMDOrder( subOffsets, subTargets, amdPerm ); // Compute the symbolic factorization of this leaf node using the // reordering just computed node.LOffsets.resize( numSources+1 ); node.LParents.resize( numSources ); vector<Int> LNnz( numSources ), Flag( numSources ), amdPermInv( numSources ); suite_sparse::ldl::Symbolic ( numSources, subOffsets.data(), subTargets.data(), node.LOffsets.data(), node.LParents.data(), LNnz.data(), Flag.data(), amdPerm.data(), amdPermInv.data() ); // Fill in this node of the local separator tree sep.off = off; sep.inds.resize( numSources ); for( Int i=0; i<numSources; ++i ) sep.inds[i] = perm[amdPerm[i]]; // Fill in this node of the local elimination tree node.size = numSources; node.off = off; set<Int> lowerStruct; for( Int s=0; s<node.size; ++s ) { const Int edgeOff = offsetBuf[s]; const Int numConn = offsetBuf[s+1] - edgeOff; for( Int t=0; t<numConn; ++t ) { const Int target = targetBuf[edgeOff+t]; if( target >= numSources ) lowerStruct.insert( off+target ); } } CopySTL( lowerStruct, node.origLowerStruct ); } else { // Partition the graph and construct the inverse map Int nxLeft, nyLeft, nzLeft, nxRight, nyRight, nzRight; Graph leftChild, rightChild; vector<Int> map; const Int sepSize = NaturalBisect ( nx, ny, nz, graph, nxLeft, nyLeft, nzLeft, leftChild, nxRight, nyRight, nzRight, rightChild, map ); vector<Int> invMap( numSources ); for( Int s=0; s<numSources; ++s ) invMap[map[s]] = s; // Mostly compute this node of the local separator tree // (we will finish computing the separator indices soon) sep.off = off + (numSources-sepSize); sep.inds.resize( sepSize ); for( Int s=0; s<sepSize; ++s ) { const Int mappedSource = s + (numSources-sepSize); sep.inds[s] = invMap[mappedSource]; } // Fill in this node in the local elimination tree node.size = sepSize; node.off = sep.off; set<Int> lowerStruct; for( Int s=0; s<sepSize; ++s ) { const Int source = sep.inds[s]; const Int edgeOff = offsetBuf[source]; const Int numConn = offsetBuf[source+1] - edgeOff; for( Int t=0; t<numConn; ++t ) { const Int target = targetBuf[edgeOff+t]; if( target >= numSources ) lowerStruct.insert( off+target ); } } CopySTL( lowerStruct, node.origLowerStruct ); // Finish computing the separator indices for( Int s=0; s<sepSize; ++s ) sep.inds[s] = perm[sep.inds[s]]; // Construct the inverse maps from the child indices to the original // degrees of freedom const Int leftChildSize = leftChild.NumSources(); vector<Int> leftPerm( leftChildSize ); for( Int s=0; s<leftChildSize; ++s ) leftPerm[s] = perm[invMap[s]]; const Int rightChildSize = rightChild.NumSources(); vector<Int> rightPerm( rightChildSize ); for( Int s=0; s<rightChildSize; ++s ) rightPerm[s] = perm[invMap[s+leftChildSize]]; sep.children.resize( 2 ); node.children.resize( 2 ); sep.children[0] = new Separator(&sep); sep.children[1] = new Separator(&sep); node.children[0] = new NodeInfo(&node); node.children[1] = new NodeInfo(&node); NaturalNestedDissectionRecursion ( nxLeft, nyLeft, nzLeft, leftChild, leftPerm, *sep.children[0], *node.children[0], off, cutoff ); NaturalNestedDissectionRecursion ( nxRight, nyRight, nzRight, rightChild, rightPerm, *sep.children[1], *node.children[1], off+leftChildSize, cutoff ); } }