416 lines
9.8 KiB
C++
416 lines
9.8 KiB
C++
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// 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<GOBJMBN_CLIQSET *>(&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<GNODEMBND *>(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;
|
||
|
}
|