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 ); } }