//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1997 - 1997 // // File: testinfo.cpp // //-------------------------------------------------------------------------- // // testinfo.cpp: test file generation // #include "testinfo.h" //////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////// /* Test inference and optionally Write an inference output file. The format is the same as the program DXTEST, which uses the older DXC32.DLL. The format is: $COMPLETE << Indicates a complete pass without instantiations Alternator,0,0.99 << One record for each state of each node, alphabetically, Alternator,1,0.01 with either the full or symbolic name (fSymName) Battery,0,0.9927 Battery,1,0.0073 Charge Delivered,0,0.95934 Charge Delivered,1,0.0406603 ... << Similar records for all other nodes ... $INSTANTIATE,Alternator,0 << Indicates a node clamped to a state Alternator,0,1 Alternator,1,0 Battery,0,0.9927 ... ... $PROBLEMINST,Engine Start,1 << Indicates a PD node instantiated $UTILITY,Node Name,3.14159 << Indicates an entropic utility record (fUtil) ... $RECOMEND,Node Name,-122.2222 << Indicates a troubleshooting recommendations record (fTSUtil) ... This routine is used to compare both timings and numerical results with the older software. The "fOutputFile" flag indicates whether an output file should be written. The "fPassCountMask" indicates how many times the loop should be performed; this value is defaulted to 1. The logic works as follows: for each pass for 1 + each problem-defining (PD) node for each non-PD node if no non-PD node is instantiated print $COMPLETE else print $INSTANTIATE and data about instantiated node for each state of each non-PD node print the name, state and value (belief) print utilities if required print recommendations if required end for each state of each non-PD node advance to the next state of the next node unclamp previous node/sate clamp (next) node to next state end for eacn non-PD node advance to the next state of the next PD node end for each problem-defining node end for each pass Note that each pass is set up so that all the uninstantiated values are printed first. */ //////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////// inline SZC TESTINFO :: SzcNdName ( GNODEMBN * pgnd ) { return FCtl() & fSymName ? pgnd->ZsrefName().Szc() : pgnd->ZsFullName().Szc(); } inline SZC TESTINFO :: SzcNdName ( ZSREF zsSymName ) { if ( FCtl() & fSymName ) return zsSymName.Szc(); GNODEMBN * pgnd; DynCastThrow( Mbnet().PgobjFind( zsSymName ), pgnd ); return SzcNdName( pgnd ); } void TESTINFO :: GetUtilities () { // Compute the utilities MbUtil()(); if ( ! Postream() ) return; const VZSREF & vzsrNodes = MbUtil().VzsrefNodes(); const VLREAL & vlrUtil = MbUtil().VlrValues(); for ( int ind = 0; ind < vzsrNodes.size(); ind++ ) { SZC szcName = SzcNdName( vzsrNodes[ind] ); Ostream() << "$UTILITY," << szcName << "," << vlrUtil[ind] << "\n"; _clOut++; } } void TESTINFO :: GetTSUtilities () { if ( ! MbRecom().BReady() ) return; // Invalid state for recommendations // Compute the utilities MbRecom()(); if ( ! Postream() ) return; const VZSREF & vzsrNodes = MbRecom().VzsrefNodes(); const VLREAL & vlrUtil = MbRecom().VlrValues(); for ( int ind = 0; ind < vzsrNodes.size(); ind++ ) { SZC szcName = SzcNdName( vzsrNodes[ind] ); Ostream() << "$RECOMMEND," << szcName << "," << vlrUtil[ind] << "\n"; _clOut++; } } // Get the beliefs for the nodes in the given map; write data records if stream given void TESTINFO :: GetBeliefs () { MDVCPD mdvBel; // Prepare to check for impossible states of information GOBJMBN_CLIQSET * pCliqueSet = NULL; if ( BFlag( fImpossible ) ) pCliqueSet = dynamic_cast(&InferEng()); // See if this state of information is impossible bool bIsImposs = pCliqueSet != NULL && pCliqueSet->BImpossible(); for ( MPSTRPND::iterator mpit = Mpstrpnd().begin(); mpit != Mpstrpnd().end(); mpit++ ) { GNODEMBND * pgndd = (*mpit).second; int cState = pgndd->CState(); if ( ! bIsImposs ) { InferEng().GetBelief( pgndd, mdvBel ); assert( cState == mdvBel.size() ); } if ( Postream() ) { SZC szcName = SzcNdName( pgndd ); for ( int ist = 0; ist < cState; ist++ ) { Ostream() << szcName << "," << ist << ","; if ( bIsImposs ) Ostream() << _rImposs; else Ostream() << mdvBel[ist]; Ostream() << "\n"; _clOut++; } } } if ( BFlag( fUtil ) ) { GetUtilities(); } else if ( BFlag( fTSUtil ) ) { GetTSUtilities(); } #ifdef _DEBUG if ( Postream() ) Ostream().flush(); #endif } void TESTINFO :: InferTest () { bool bOutput = Postream() != NULL; int cPass = FCtl() & fPassCountMask; // Is network expanded? bool bExpanded = Mbnet().BFlag( EIBF_Expanded ); PROPMGR propmgr( Mbnet() ); // Property manager int iLblProblem = propmgr.ILblToUser( ESTDLBL_problem ); ZSREF zsrPropTypeLabel = propmgr.ZsrPropType( ESTDP_label ); MPSTRPND & mpstrpnd = Mpstrpnd(); // Map of strings to node ptrs MPSTRPND mpstrpndProblem; // Map of PD nodes for ( int inode = 0; inode < Mbnet().CNameMax(); inode++ ) { GOBJMBN * pgobj = Mbnet().PgobjFindByIndex( inode ); if ( ! pgobj ) continue; GNODEMBND * pgndd = dynamic_cast(pgobj); if ( ! pgndd ) continue; SZC szcName = FCtl() & fSymName ? pgndd->ZsrefName().Szc() : pgndd->ZsFullName().Szc(); // See if this is a problem-defining node PROPMBN * propLbl = pgndd->LtProp().PFind( zsrPropTypeLabel ); if ( propLbl && propLbl->Real() == iLblProblem ) { // Put PD nodes into separate map mpstrpndProblem[szcName] = pgndd; } // If the network is expanded, use only regular nodes if ( (! bExpanded) || ! pgndd->BFlag( EIBF_Expansion ) ) { mpstrpnd[szcName] = pgndd; } } for ( int iPass = 0; iPass < cPass; iPass++ ) { int iProb = -1; int iProbState = 0; int cProbState = 0; GNODEMBND * pgnddProblem = NULL; MPSTRPND::iterator mpitPd = mpstrpndProblem.begin(); MPSTRPND::iterator mpitPdEnd = mpstrpndProblem.end(); for (;;) { // After 1st cycle, advance the problem state of the PD node if ( pgnddProblem ) { ZSTR zsNamePD; CLAMP clampProblemState(true, iProbState, true); InferEng().EnterEvidence( pgnddProblem, clampProblemState ); if ( FCtl() & fSymName ) zsNamePD = pgnddProblem->ZsrefName(); else zsNamePD = pgnddProblem->ZsFullName(); if ( bOutput ) { Ostream() << "$PROBLEMINST," << zsNamePD.Szc() << "," << iProbState << "\n"; _clOut++; } } MPSTRPND::iterator mpit = mpstrpnd.begin(); MPSTRPND::iterator mpend = mpstrpnd.end(); int cpnd = mpstrpnd.size(); for ( int inid = -1; inid < cpnd; inid++ ) { GNODEMBND * pgndd = NULL; ZSTR zsName; int cst = 0; // Cause inner loop to run once on first cycle if ( inid >= 0 ) { pgndd = (*mpit++).second; if ( FCtl() & fSymName ) zsName = pgndd->ZsrefName(); else zsName = pgndd->ZsFullName(); cst = pgndd->CState(); } for ( int ist = -1; ist < cst; ist++ ) { if ( ist < 0 ) { // The first time through, print all the beliefs // with no instantiations; do nothing on later cycles. if ( pgndd != NULL ) continue; if ( bOutput ) { Ostream() << "$COMPLETE\n"; _clOut++; } } else { CLAMP clampState(true, ist, true); InferEng().EnterEvidence( pgndd, clampState ); if ( bOutput ) { Ostream() << "$INSTANTIATE," << zsName.Szc() << "," << ist << "\n"; _clOut++; } } GetBeliefs(); } if ( pgndd ) { // Clear the instantitation of this node. InferEng().EnterEvidence( pgndd, CLAMP() ); } } // If this is the last abnormal state for this problem node, // advance to the next node. if ( ++iProbState >= cProbState ) { // Unclamp the last problem node, if any if ( pgnddProblem ) InferEng().EnterEvidence( pgnddProblem, CLAMP() ); // Move on to the next PD node if ( mpitPd == mpitPdEnd ) break; pgnddProblem = (*mpitPd++).second; cProbState = pgnddProblem->CState(); // Reset to 1st problem state iProbState = 1; } } } } // Return a displayable string of the current options settings ZSTR TESTINFO :: ZsOptions ( ULONG fFlag ) { static struct { ULONG _f; // Bit flag SZC _szc; // Option name } vOptMap [] = { { fVerbose, "verbose" }, { fCliquing, "clique" }, { fInference, "infer" }, { fMulti, "multipass" }, { fOutputFile, "outfile" }, { fShowTime, "times" }, { fSaveDsc, "dscout" }, { fPause, "pause" }, { fSymName, "symname" }, { fExpand, "expand" }, { fClone, "clone" }, { fUtil, "utilities" }, { fReg, "registry" }, { fTSUtil, "recommend" }, { fInferStats, "inferstats"}, { fImpossible, "impossible"}, { 0, "" } }; ZSTR zs; ULONG cpass = fFlag & fPassCountMask; fFlag &= ~ fPassCountMask; for ( int i = 0; vOptMap[i]._f != 0; i++ ) { if ( fFlag & vOptMap[i]._f ) { if ( zs.length() > 0 ) zs += ','; zs += vOptMap[i]._szc; } } if ( fFlag & fMulti ) { if ( zs.length() > 0 ) zs += ","; zs.FormatAppend("passes=%d", cpass); } return zs; }