/**CFile**************************************************************** FileName [retFlow.c] SystemName [ABC: Logic synthesis and verification system.] PackageName [Network and node package.] Synopsis [Implementation of maximum flow (min-area retiming).] Author [Alan Mishchenko] Affiliation [UC Berkeley] Date [Ver. 1.0. Started - Oct 31, 2006.] Revision [$Id: retFlow.c,v 1.00 2006/10/31 00:00:00 alanmi Exp $] ***********************************************************************/ #include "retInt.h" ABC_NAMESPACE_IMPL_START //////////////////////////////////////////////////////////////////////// /// DECLARATIONS /// //////////////////////////////////////////////////////////////////////// static inline int Abc_ObjSetPath( Abc_Obj_t * pObj, Abc_Obj_t * pNext ) { pObj->pCopy = pNext; return 1; } static inline Abc_Obj_t * Abc_ObjGetPath( Abc_Obj_t * pObj ) { return pObj->pCopy; } static inline Abc_Obj_t * Abc_ObjGetFanoutPath( Abc_Obj_t * pObj ) { Abc_Obj_t * pFanout; int i; assert( Abc_ObjGetPath(pObj) ); Abc_ObjForEachFanout( pObj, pFanout, i ) if ( Abc_ObjGetPath(pFanout) == pObj ) return pFanout; return NULL; } static inline Abc_Obj_t * Abc_ObjGetFaninPath( Abc_Obj_t * pObj ) { Abc_Obj_t * pFanin; int i; assert( Abc_ObjGetPath(pObj) ); Abc_ObjForEachFanin( pObj, pFanin, i ) if ( Abc_ObjGetPath(pFanin) == pObj ) return pFanin; return NULL; } static inline Abc_Obj_t * Abc_ObjGetPredecessorBwd( Abc_Obj_t * pObj ) { Abc_Obj_t * pNext; int i; Abc_ObjForEachFanout( pObj, pNext, i ) if ( Abc_ObjGetPath(pNext) == pObj ) return pNext; Abc_ObjForEachFanin( pObj, pNext, i ) if ( Abc_ObjGetPath(pNext) == pObj ) return pNext; return NULL; } static inline Abc_Obj_t * Abc_ObjGetPredecessorFwd( Abc_Obj_t * pObj ) { Abc_Obj_t * pNext; int i; Abc_ObjForEachFanin( pObj, pNext, i ) if ( Abc_ObjGetPath(pNext) == pObj ) return pNext; Abc_ObjForEachFanout( pObj, pNext, i ) if ( Abc_ObjGetPath(pNext) == pObj ) return pNext; return NULL; } static int Abc_NtkMaxFlowBwdPath_rec( Abc_Obj_t * pObj ); static int Abc_NtkMaxFlowFwdPath_rec( Abc_Obj_t * pObj ); static int Abc_NtkMaxFlowBwdPath2_rec( Abc_Obj_t * pObj ); static int Abc_NtkMaxFlowFwdPath2_rec( Abc_Obj_t * pObj ); //static int Abc_NtkMaxFlowBwdPath3_rec( Abc_Obj_t * pObj ); static int Abc_NtkMaxFlowFwdPath3_rec( Abc_Obj_t * pObj, Abc_Obj_t * pPrev, int fFanin ); static Vec_Ptr_t * Abc_NtkMaxFlowMinCut( Abc_Ntk_t * pNtk, int fForward ); static void Abc_NtkMaxFlowMinCutUpdate( Abc_Ntk_t * pNtk, Vec_Ptr_t * vMinCut, int fForward ); static int Abc_NtkMaxFlowVerifyCut( Abc_Ntk_t * pNtk, Vec_Ptr_t * vMinCut, int fForward ); static void Abc_NtkMaxFlowPrintCut( Abc_Ntk_t * pNtk, Vec_Ptr_t * vMinCut ); static void Abc_NtkMaxFlowPrintFlow( Abc_Ntk_t * pNtk, int fForward ); //////////////////////////////////////////////////////////////////////// /// FUNCTION DEFINITIONS /// //////////////////////////////////////////////////////////////////////// /**Function************************************************************* Synopsis [Test-bench for the max-flow computation.] Description [] SideEffects [] SeeAlso [] ***********************************************************************/ void Abc_NtkMaxFlowTest( Abc_Ntk_t * pNtk ) { Vec_Ptr_t * vMinCut; Abc_Obj_t * pObj; int i; // forward flow Abc_NtkForEachPo( pNtk, pObj, i ) pObj->fMarkA = 1; Abc_NtkForEachLatch( pNtk, pObj, i ) pObj->fMarkA = Abc_ObjFanin0(pObj)->fMarkA = 1; // Abc_ObjFanin0(pObj)->fMarkA = 1; vMinCut = Abc_NtkMaxFlow( pNtk, 1, 1 ); Vec_PtrFree( vMinCut ); Abc_NtkCleanMarkA( pNtk ); // backward flow Abc_NtkForEachPi( pNtk, pObj, i ) pObj->fMarkA = 1; Abc_NtkForEachLatch( pNtk, pObj, i ) pObj->fMarkA = Abc_ObjFanout0(pObj)->fMarkA = 1; // Abc_ObjFanout0(pObj)->fMarkA = 1; vMinCut = Abc_NtkMaxFlow( pNtk, 0, 1 ); Vec_PtrFree( vMinCut ); Abc_NtkCleanMarkA( pNtk ); } /**Function************************************************************* Synopsis [Implementation of max-flow/min-cut computation.] Description [] SideEffects [] SeeAlso [] ***********************************************************************/ Vec_Ptr_t * Abc_NtkMaxFlow( Abc_Ntk_t * pNtk, int fForward, int fVerbose ) { Vec_Ptr_t * vMinCut; Abc_Obj_t * pLatch; int Flow, FlowCur, RetValue, i; abctime clk = Abc_Clock(); int fUseDirectedFlow = 1; // find the max-flow Abc_NtkCleanCopy( pNtk ); Flow = 0; Abc_NtkIncrementTravId(pNtk); Abc_NtkForEachLatch( pNtk, pLatch, i ) { if ( fForward ) { // assert( !Abc_ObjFanout0(pLatch)->fMarkA ); FlowCur = Abc_NtkMaxFlowFwdPath2_rec( Abc_ObjFanout0(pLatch) ); // FlowCur = Abc_NtkMaxFlowFwdPath3_rec( Abc_ObjFanout0(pLatch), pLatch, 1 ); Flow += FlowCur; } else { assert( !Abc_ObjFanin0(pLatch)->fMarkA ); FlowCur = Abc_NtkMaxFlowBwdPath2_rec( Abc_ObjFanin0(pLatch) ); Flow += FlowCur; } if ( FlowCur ) Abc_NtkIncrementTravId(pNtk); } if ( !fUseDirectedFlow ) { Abc_NtkIncrementTravId(pNtk); Abc_NtkForEachLatch( pNtk, pLatch, i ) { if ( fForward ) { // assert( !Abc_ObjFanout0(pLatch)->fMarkA ); FlowCur = Abc_NtkMaxFlowFwdPath_rec( Abc_ObjFanout0(pLatch) ); Flow += FlowCur; } else { assert( !Abc_ObjFanin0(pLatch)->fMarkA ); FlowCur = Abc_NtkMaxFlowBwdPath_rec( Abc_ObjFanin0(pLatch) ); Flow += FlowCur; } if ( FlowCur ) Abc_NtkIncrementTravId(pNtk); } } // Abc_NtkMaxFlowPrintFlow( pNtk, fForward ); // mark the nodes reachable from the latches Abc_NtkIncrementTravId(pNtk); Abc_NtkForEachLatch( pNtk, pLatch, i ) { if ( fForward ) { // assert( !Abc_ObjFanout0(pLatch)->fMarkA ); if ( fUseDirectedFlow ) RetValue = Abc_NtkMaxFlowFwdPath2_rec( Abc_ObjFanout0(pLatch) ); // RetValue = Abc_NtkMaxFlowFwdPath3_rec( Abc_ObjFanout0(pLatch), pLatch, 1 ); else RetValue = Abc_NtkMaxFlowFwdPath_rec( Abc_ObjFanout0(pLatch) ); } else { assert( !Abc_ObjFanin0(pLatch)->fMarkA ); if ( fUseDirectedFlow ) RetValue = Abc_NtkMaxFlowBwdPath2_rec( Abc_ObjFanin0(pLatch) ); else RetValue = Abc_NtkMaxFlowBwdPath_rec( Abc_ObjFanin0(pLatch) ); } assert( RetValue == 0 ); } // find the min-cut with the smallest volume vMinCut = Abc_NtkMaxFlowMinCut( pNtk, fForward ); // verify the cut if ( !Abc_NtkMaxFlowVerifyCut(pNtk, vMinCut, fForward) ) printf( "Abc_NtkMaxFlow() error! The computed min-cut is not a cut!\n" ); // make the cut retimable Abc_NtkMaxFlowMinCutUpdate( pNtk, vMinCut, fForward ); // report the results if ( fVerbose ) { printf( "L = %6d. %s max-flow = %6d. Min-cut = %6d. ", Abc_NtkLatchNum(pNtk), fForward? "Forward " : "Backward", Flow, Vec_PtrSize(vMinCut) ); ABC_PRT( "Time", Abc_Clock() - clk ); } // Abc_NtkMaxFlowPrintCut( pNtk, vMinCut ); return vMinCut; } /**Function************************************************************* Synopsis [Tries to find an augmenting path originating in this node.] Description [] SideEffects [] SeeAlso [] ***********************************************************************/ int Abc_NtkMaxFlowBwdPath_rec( Abc_Obj_t * pObj ) { Abc_Obj_t * pNext, * pPred; int i; // skip visited nodes if ( Abc_NodeIsTravIdCurrent(pObj) ) return 0; Abc_NodeSetTravIdCurrent(pObj); // get the predecessor pPred = Abc_ObjGetPredecessorBwd( pObj ); // process node without flow if ( !Abc_ObjGetPath(pObj) ) { // start the path if we reached a terminal node if ( pObj->fMarkA ) return Abc_ObjSetPath( pObj, (Abc_Obj_t *)1 ); // explore the fanins Abc_ObjForEachFanin( pObj, pNext, i ) if ( pNext != pPred && !Abc_ObjIsLatch(pNext) && Abc_NtkMaxFlowBwdPath_rec(pNext) ) return Abc_ObjSetPath( pObj, pNext ); Abc_ObjForEachFanout( pObj, pNext, i ) if ( pNext != pPred && !Abc_ObjIsLatch(pNext) && Abc_NtkMaxFlowBwdPath_rec(pNext) ) return Abc_ObjSetPath( pObj, pNext ); return 0; } // pObj has flow - find the fanout with flow if ( pPred == NULL ) return 0; // go through the successors of the predecessor Abc_ObjForEachFanin( pPred, pNext, i ) if ( !Abc_ObjIsLatch(pNext) && Abc_NtkMaxFlowBwdPath_rec( pNext ) ) return Abc_ObjSetPath( pPred, pNext ); Abc_ObjForEachFanout( pPred, pNext, i ) if ( !Abc_ObjIsLatch(pNext) && Abc_NtkMaxFlowBwdPath_rec( pNext ) ) return Abc_ObjSetPath( pPred, pNext ); // try the fanout if ( Abc_NtkMaxFlowBwdPath_rec( pPred ) ) return Abc_ObjSetPath( pPred, NULL ); return 0; } /**Function************************************************************* Synopsis [Tries to find an augmenting path originating in this node.] Description [] SideEffects [] SeeAlso [] ***********************************************************************/ int Abc_NtkMaxFlowFwdPath_rec( Abc_Obj_t * pObj ) { Abc_Obj_t * pNext, * pPred; int i; // skip visited nodes if ( Abc_NodeIsTravIdCurrent(pObj) ) return 0; Abc_NodeSetTravIdCurrent(pObj); // get the predecessor pPred = Abc_ObjGetPredecessorFwd( pObj ); // process node without flow if ( !Abc_ObjGetPath(pObj) ) { // start the path if we reached a terminal node if ( pObj->fMarkA ) return Abc_ObjSetPath( pObj, (Abc_Obj_t *)1 ); // explore the fanins Abc_ObjForEachFanout( pObj, pNext, i ) if ( pNext != pPred && !Abc_ObjIsLatch(pNext) && Abc_NtkMaxFlowFwdPath_rec(pNext) ) return Abc_ObjSetPath( pObj, pNext ); Abc_ObjForEachFanin( pObj, pNext, i ) if ( pNext != pPred && !Abc_ObjIsLatch(pNext) && Abc_NtkMaxFlowFwdPath_rec(pNext) ) return Abc_ObjSetPath( pObj, pNext ); return 0; } // pObj has flow - find the fanout with flow if ( pPred == NULL ) return 0; // go through the successors of the predecessor Abc_ObjForEachFanout( pPred, pNext, i ) if ( !Abc_ObjIsLatch(pNext) && Abc_NtkMaxFlowFwdPath_rec( pNext ) ) return Abc_ObjSetPath( pPred, pNext ); Abc_ObjForEachFanin( pPred, pNext, i ) if ( !Abc_ObjIsLatch(pNext) && Abc_NtkMaxFlowFwdPath_rec( pNext ) ) return Abc_ObjSetPath( pPred, pNext ); // try the fanout if ( Abc_NtkMaxFlowFwdPath_rec( pPred ) ) return Abc_ObjSetPath( pPred, NULL ); return 0; } /**Function************************************************************* Synopsis [Tries to find an augmenting path originating in this edge.] Description [] SideEffects [] SeeAlso [] ***********************************************************************/ int Abc_NtkMaxFlowFwdPath3_rec( Abc_Obj_t * pObj, Abc_Obj_t * pPrev, int fFanin ) { Abc_Obj_t * pFanin, * pFanout; int i; // skip visited nodes if ( Abc_NodeIsTravIdCurrent(pObj) ) return 0; Abc_NodeSetTravIdCurrent(pObj); // skip the fanin which already has flow if ( fFanin && Abc_ObjGetPath(pPrev) ) return 0; // if the node has no flow, try to push through the fanouts if ( !Abc_ObjGetPath(pObj) ) { // start the path if we reached a terminal node if ( pObj->fMarkA ) return Abc_ObjSetPath( pObj, (Abc_Obj_t *)1 ); // try to push flow through the fanouts Abc_ObjForEachFanout( pObj, pFanout, i ) if ( Abc_NtkMaxFlowFwdPath3_rec(pFanout, pObj, 1) ) return fFanin? Abc_ObjSetPath(pPrev, pObj) : 1; } // try to push through the fanins Abc_ObjForEachFanin( pObj, pFanin, i ) if ( !Abc_ObjIsLatch(pFanin) && Abc_NtkMaxFlowFwdPath3_rec(pFanin, pObj, 0) ) return Abc_ObjSetPath( pFanin, NULL ); return 0; } /**Function************************************************************* Synopsis [Tries to find an augmenting path originating in this node.] Description [This procedure works for directed graphs only!] SideEffects [] SeeAlso [] ***********************************************************************/ int Abc_NtkMaxFlowBwdPath2_rec( Abc_Obj_t * pObj ) { Abc_Obj_t * pFanout, * pFanin; int i; // skip visited nodes if ( Abc_NodeIsTravIdCurrent(pObj) ) return 0; Abc_NodeSetTravIdCurrent(pObj); // process node without flow if ( !Abc_ObjGetPath(pObj) ) { // start the path if we reached a terminal node if ( pObj->fMarkA ) return Abc_ObjSetPath( pObj, (Abc_Obj_t *)1 ); // explore the fanins Abc_ObjForEachFanin( pObj, pFanin, i ) if ( Abc_NtkMaxFlowBwdPath2_rec(pFanin) ) return Abc_ObjSetPath( pObj, pFanin ); return 0; } // pObj has flow - find the fanout with flow pFanout = Abc_ObjGetFanoutPath( pObj ); if ( pFanout == NULL ) return 0; // go through the fanins of the fanout with flow Abc_ObjForEachFanin( pFanout, pFanin, i ) if ( Abc_NtkMaxFlowBwdPath2_rec( pFanin ) ) return Abc_ObjSetPath( pFanout, pFanin ); // try the fanout if ( Abc_NtkMaxFlowBwdPath2_rec( pFanout ) ) return Abc_ObjSetPath( pFanout, NULL ); return 0; } /**Function************************************************************* Synopsis [Tries to find an augmenting path originating in this node.] Description [This procedure works for directed graphs only!] SideEffects [] SeeAlso [] ***********************************************************************/ int Abc_NtkMaxFlowFwdPath2_rec( Abc_Obj_t * pObj ) { Abc_Obj_t * pFanout, * pFanin; int i; // skip visited nodes if ( Abc_NodeIsTravIdCurrent(pObj) ) return 0; Abc_NodeSetTravIdCurrent(pObj); // process node without flow if ( !Abc_ObjGetPath(pObj) ) { // start the path if we reached a terminal node if ( pObj->fMarkA ) return Abc_ObjSetPath( pObj, (Abc_Obj_t *)1 ); // explore the fanins Abc_ObjForEachFanout( pObj, pFanout, i ) if ( Abc_NtkMaxFlowFwdPath2_rec(pFanout) ) return Abc_ObjSetPath( pObj, pFanout ); return 0; } // pObj has flow - find the fanout with flow pFanin = Abc_ObjGetFaninPath( pObj ); if ( pFanin == NULL ) return 0; // go through the fanins of the fanout with flow Abc_ObjForEachFanout( pFanin, pFanout, i ) if ( Abc_NtkMaxFlowFwdPath2_rec( pFanout ) ) return Abc_ObjSetPath( pFanin, pFanout ); // try the fanout if ( Abc_NtkMaxFlowFwdPath2_rec( pFanin ) ) return Abc_ObjSetPath( pFanin, NULL ); return 0; } /**Function************************************************************* Synopsis [Find minimum-volume minumum cut.] Description [] SideEffects [] SeeAlso [] ***********************************************************************/ Vec_Ptr_t * Abc_NtkMaxFlowMinCut( Abc_Ntk_t * pNtk, int fForward ) { Vec_Ptr_t * vMinCut; Abc_Obj_t * pObj; int i; // collect the cut nodes vMinCut = Vec_PtrAlloc( Abc_NtkLatchNum(pNtk) ); Abc_NtkForEachObj( pNtk, pObj, i ) { // node without flow is not a cut node if ( !Abc_ObjGetPath(pObj) ) continue; // unvisited node is below the cut if ( !Abc_NodeIsTravIdCurrent(pObj) ) continue; // add terminal with flow or node whose path is not visited if ( pObj->fMarkA || !Abc_NodeIsTravIdCurrent( Abc_ObjGetPath(pObj) ) ) Vec_PtrPush( vMinCut, pObj ); } return vMinCut; } /**Function************************************************************* Synopsis [Marks the TFI cone with MarkA.] Description [] SideEffects [] SeeAlso [] ***********************************************************************/ void Abc_NtkMaxFlowMarkCut_rec( Abc_Obj_t * pObj ) { Abc_Obj_t * pNext; int i; if ( pObj->fMarkA ) return; pObj->fMarkA = 1; Abc_ObjForEachFanin( pObj, pNext, i ) Abc_NtkMaxFlowMarkCut_rec( pNext ); } /**Function************************************************************* Synopsis [Visits the TFI up to marked nodes and collects marked nodes.] Description [] SideEffects [] SeeAlso [] ***********************************************************************/ void Abc_NtkMaxFlowCollectCut_rec( Abc_Obj_t * pObj, Vec_Ptr_t * vNodes ) { Abc_Obj_t * pNext; int i; if ( Abc_NodeIsTravIdCurrent(pObj) ) return; Abc_NodeSetTravIdCurrent(pObj); if ( pObj->fMarkA ) { Vec_PtrPush( vNodes, pObj ); return; } Abc_ObjForEachFanin( pObj, pNext, i ) Abc_NtkMaxFlowCollectCut_rec( pNext, vNodes ); } /**Function************************************************************* Synopsis [Updates the minimum cut to be retimable.] Description [This procedure also labels the nodes reachable from the latches to the cut with fMarkA.] SideEffects [] SeeAlso [] ***********************************************************************/ void Abc_NtkMaxFlowMinCutUpdate( Abc_Ntk_t * pNtk, Vec_Ptr_t * vMinCut, int fForward ) { Abc_Obj_t * pObj, * pNext; int i, k; // clean marks Abc_NtkForEachObj( pNtk, pObj, i ) pObj->fMarkA = 0; // set latch outputs Abc_NtkForEachLatch( pNtk, pObj, i ) Abc_ObjFanout0(pObj)->fMarkA = 1; // traverse from cut nodes Vec_PtrForEachEntry( Abc_Obj_t *, vMinCut, pObj, i ) Abc_NtkMaxFlowMarkCut_rec( pObj ); if ( fForward ) { // change mincut to be nodes with unmarked fanouts Vec_PtrClear( vMinCut ); Abc_NtkForEachObj( pNtk, pObj, i ) { if ( !pObj->fMarkA ) continue; Abc_ObjForEachFanout( pObj, pNext, k ) { if ( pNext->fMarkA ) continue; Vec_PtrPush( vMinCut, pObj ); break; } } } else { // change mincut to be marked fanins of the unmarked nodes Vec_PtrClear( vMinCut ); Abc_NtkIncrementTravId(pNtk); Abc_NtkForEachLatch( pNtk, pObj, i ) Abc_NtkMaxFlowCollectCut_rec( Abc_ObjFanin0(pObj), vMinCut ); // transfer the attribute Abc_NtkForEachObj( pNtk, pObj, i ) pObj->fMarkA = Abc_NodeIsTravIdCurrent(pObj); // unmark the cut nodes Vec_PtrForEachEntry( Abc_Obj_t *, vMinCut, pObj, i ) pObj->fMarkA = 0; } } /**Function************************************************************* Synopsis [Verifies the min-cut is indeed a cut.] Description [] SideEffects [] SeeAlso [] ***********************************************************************/ int Abc_NtkMaxFlowVerifyCut_rec( Abc_Obj_t * pObj, int fForward ) { Abc_Obj_t * pNext; int i; // skip visited nodes if ( Abc_NodeIsTravIdCurrent(pObj) ) return 1; Abc_NodeSetTravIdCurrent(pObj); // visit the node if ( fForward ) { if ( Abc_ObjIsCo(pObj) ) return 0; // explore the fanouts Abc_ObjForEachFanout( pObj, pNext, i ) if ( !Abc_NtkMaxFlowVerifyCut_rec(pNext, fForward) ) return 0; } else { if ( Abc_ObjIsCi(pObj) ) return 0; // explore the fanins Abc_ObjForEachFanin( pObj, pNext, i ) if ( !Abc_NtkMaxFlowVerifyCut_rec(pNext, fForward) ) return 0; } return 1; } /**Function************************************************************* Synopsis [Verifies the min-cut is indeed a cut.] Description [] SideEffects [] SeeAlso [] ***********************************************************************/ int Abc_NtkMaxFlowVerifyCut( Abc_Ntk_t * pNtk, Vec_Ptr_t * vMinCut, int fForward ) { Abc_Obj_t * pObj; int i; // mark the cut with the current traversal ID Abc_NtkIncrementTravId(pNtk); Vec_PtrForEachEntry( Abc_Obj_t *, vMinCut, pObj, i ) Abc_NodeSetTravIdCurrent( pObj ); // search from the latches for a path to the COs/CIs Abc_NtkForEachLatch( pNtk, pObj, i ) { if ( fForward ) { if ( !Abc_NtkMaxFlowVerifyCut_rec( Abc_ObjFanout0(pObj), fForward ) ) return 0; } else { if ( !Abc_NtkMaxFlowVerifyCut_rec( Abc_ObjFanin0(pObj), fForward ) ) return 0; } } /* { // count the volume of the cut int Counter = 0; Abc_NtkForEachObj( pNtk, pObj, i ) Counter += Abc_NodeIsTravIdCurrent( pObj ); printf( "Volume = %d.\n", Counter ); } */ return 1; } /**Function************************************************************* Synopsis [Prints the flows.] Description [] SideEffects [] SeeAlso [] ***********************************************************************/ void Abc_NtkMaxFlowPrintFlow( Abc_Ntk_t * pNtk, int fForward ) { Abc_Obj_t * pLatch, * pNext; Abc_Obj_t * pPrev = NULL; // Suppress "might be used uninitialized" int i; if ( fForward ) { Vec_PtrForEachEntry( Abc_Obj_t *, pNtk->vBoxes, pLatch, i ) { assert( !Abc_ObjFanout0(pLatch)->fMarkA ); if ( Abc_ObjGetPath(Abc_ObjFanout0(pLatch)) == NULL ) // no flow through this latch continue; printf( "Path = " ); for ( pNext = Abc_ObjFanout0(pLatch); pNext != (void *)1; pNext = Abc_ObjGetPath(pNext) ) { printf( "%s(%d) ", Abc_ObjName(pNext), pNext->Id ); pPrev = pNext; } if ( !Abc_ObjIsPo(pPrev) ) printf( "%s(%d) ", Abc_ObjName(Abc_ObjFanout0(pPrev)), Abc_ObjFanout0(pPrev)->Id ); printf( "\n" ); } } else { Vec_PtrForEachEntry( Abc_Obj_t *, pNtk->vBoxes, pLatch, i ) { assert( !Abc_ObjFanin0(pLatch)->fMarkA ); if ( Abc_ObjGetPath(Abc_ObjFanin0(pLatch)) == NULL ) // no flow through this latch continue; printf( "Path = " ); for ( pNext = Abc_ObjFanin0(pLatch); pNext != (void *)1; pNext = Abc_ObjGetPath(pNext) ) { printf( "%s(%d) ", Abc_ObjName(pNext), pNext->Id ); pPrev = pNext; } if ( !Abc_ObjIsPi(pPrev) ) printf( "%s(%d) ", Abc_ObjName(Abc_ObjFanin0(pPrev)), Abc_ObjFanin0(pPrev)->Id ); printf( "\n" ); } } } /**Function************************************************************* Synopsis [Prints the min-cut.] Description [] SideEffects [] SeeAlso [] ***********************************************************************/ void Abc_NtkMaxFlowPrintCut( Abc_Ntk_t * pNtk, Vec_Ptr_t * vMinCut ) { Abc_Obj_t * pObj; int i; printf( "Min-cut: " ); Vec_PtrForEachEntry( Abc_Obj_t *, vMinCut, pObj, i ) printf( "%s(%d) ", Abc_ObjName(pObj), pObj->Id ); printf( "\n" ); printf( "Marked nodes: " ); Abc_NtkForEachObj( pNtk, pObj, i ) if ( pObj->fMarkA ) printf( "%s(%d) ", Abc_ObjName(pObj), pObj->Id ); printf( "\n" ); } //////////////////////////////////////////////////////////////////////// /// END OF FILE /// //////////////////////////////////////////////////////////////////////// ABC_NAMESPACE_IMPL_END