windows-nt/Source/XPSP1/NT/enduser/troubleshoot/bn/mbnetdsc.cpp
2020-09-26 16:20:57 +08:00

679 lines
16 KiB
C++

//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1997 - 1998
//
// File: mbnetdsc.cpp
//
//--------------------------------------------------------------------------
//
// MBNETDSC.CPP: MBNETDSC functions
//
#include <basetsd.h>
#include "gmobj.h"
#include "bnparse.h"
MBNETDSC :: MBNETDSC ()
: _pfDsc(NULL)
{
}
MBNETDSC :: ~ MBNETDSC ()
{
}
static
struct TKNFUNCMAP
{
BNDIST::EDIST ed;
SZC szc;
}
rgTknFunc[] =
{
{ BNDIST::ED_CI_MAX, "max" },
{ BNDIST::ED_CI_PLUS, "plus" },
{ BNDIST::ED_MAX, NULL } // must be last
};
SZC MBNETDSC :: SzcDist ( BNDIST::EDIST edist )
{
for ( int i = 0; rgTknFunc[i].szc; i++ )
{
if ( rgTknFunc[i].ed == edist )
break;
}
return rgTknFunc[i].szc;
}
//
// String-to-token translation.
//
struct TKNMAP
{
SZC _szc;
TOKEN _token;
};
static TKNMAP rgTknStr[] =
{ // This table must be kept in alphabetic order
"", tokenNil,
"..", tokenRangeOp,
"array", tokenArray,
"as", tokenAs,
"branch", tokenBranch,
"choice", tokenWordChoice,
"continuous", tokenContinuous,
"creator", tokenCreator,
"decisionGraph", tokenDecisionGraph,
"default", tokenDefault,
"discrete", tokenDiscrete,
"distribution", tokenDistribution,
"domain", tokenDomain,
"for", tokenFor,
"format", tokenFormat,
"function", tokenFunction,
"import", tokenImport,
"is", tokenIs,
"leaf", tokenLeaf,
"leak", tokenLeak,
"level", tokenLevel,
"merge", tokenMerge,
"multinoulli", tokenMultinoulli,
"na", tokenNA,
"name", tokenName,
"named", tokenNamed,
"network", tokenNetwork,
"node", tokenNode,
"of", tokenOf,
"on", tokenOn,
"parent", tokenParent,
"position", tokenPosition,
"probability", tokenProbability,
"properties", tokenProperties,
"property", tokenProperty,
"real", tokenWordReal,
"standard", tokenStandard,
"state", tokenState,
"string", tokenWordString,
"type", tokenType,
"version", tokenVersion,
"vertex", tokenVertex,
"with", tokenWith,
NULL, tokenNil // must be last one
};
//
// Map a string to a token (case-sensitive)
//
TOKEN MBNETDSC :: TokenFind ( SZC szc )
{
static bool bFirstTime = true;
assert( szc != NULL );
TKNMAP * ptknmap;
if ( bFirstTime )
{
// Verify that the parser token table is in sequence
bFirstTime = false;
TKNMAP * ptknmapLast = NULL;
for ( ptknmap = rgTknStr;
ptknmap->_szc;
++ptknmap)
{
ASSERT_THROW( ptknmapLast == NULL
|| ::strcmp( ptknmapLast->_szc, ptknmap->_szc ) < 0,
EC_INTERNAL_ERROR,
"parser token table out of sequence" );
ptknmapLast = ptknmap;
}
}
for ( ptknmap = rgTknStr;
ptknmap->_szc;
++ptknmap)
{
int i = ::strcmp(szc, ptknmap->_szc);
if ( i > 0 )
continue;
if ( i == 0 )
break;
return tokenNil;
}
return ptknmap->_token;
}
//
// Map a token to a string.
//
SZC MBNETDSC :: SzcTokenMap ( TOKEN tkn )
{
for ( TKNMAP * ptknmap = rgTknStr;
ptknmap->_szc;
++ptknmap)
{
if ( ptknmap->_token == tkn )
break;
}
return ptknmap->_szc;
}
bool MBNETDSC :: BParse ( SZC szcFn, FILE * pfErr )
{
if ( pfErr == NULL )
pfErr = stderr;
PARSIN_DSC flpIn;
PARSOUT_STD flpOut(pfErr);
DSCPARSER parser(self, flpIn, flpOut);
UINT cError, cWarning;
return parser.BInitOpen( szcFn )
&& parser.BParse( cError, cWarning );
}
void MBNETDSC :: Print ( FILE * pf )
{
if ( ! pf )
pf = stdout;
_pfDsc = pf;
PrintHeaderBlock();
PrintPropertyDeclarations();
PrintDomains();
PrintNodes();
PrintTopologyAndDistributions();
_pfDsc = NULL;
}
void MBNETDSC :: PrintDomains ()
{
MBNET::ITER mbnit( self, GOBJMBN::EBNO_VARIABLE_DOMAIN );
GOBJMBN * pgmobj;
ZSTR zstrRange;
for ( ; pgmobj = *mbnit ; ++mbnit)
{
ZSREF zsrName = mbnit.ZsrCurrent();
GOBJMBN_DOMAIN * pgdom;
DynCastThrow( pgmobj, pgdom );
fprintf( _pfDsc, "\ndomain %s\n{", zsrName.Szc() );
const RDOMAIN & rdom = pgdom->Domain();
RDOMAIN::const_iterator itdm = rdom.begin();
for ( int i = 0; itdm != rdom.end(); i++ )
{
const RANGEDEF & rdef = *itdm;
zstrRange.Reset();
// If the range is a singleton and is the next integer,
// just print it as-is.
if ( ! rdef.BDiscrete() || rdef.IDiscrete() != i )
{
// Format the range operator and arguments
if ( rdef.BDiscrete() )
{
zstrRange.Format( "%d", rdef.IDiscrete() );
}
else
{
if ( rdef.BLbound() )
zstrRange.FormatAppend( "%g ", rdef.RLbound() );
zstrRange.FormatAppend( "%s", SzcTokenMap(tokenRangeOp) );
if ( rdef.BUbound() )
zstrRange.FormatAppend( " %g", rdef.RUbound() );
}
zstrRange.FormatAppend(" : ");
}
fprintf( _pfDsc, "\n\t%s\"%s\"", zstrRange.Szc(), rdef.ZsrName().Szc() );
if ( ++itdm != rdom.end() )
fprintf( _pfDsc, "," );
}
fprintf( _pfDsc, "\n}\n" );
}
}
void MBNETDSC :: PrintHeaderBlock()
{
fprintf(_pfDsc, "%s", SzcTokenMap(tokenNetwork) );
if ( ZsNetworkID().length() > 0 )
{
fprintf(_pfDsc, " \"%s\"", ZsNetworkID().Szc() );
}
fprintf(_pfDsc, "\n{");
if ( RVersion() >= 0.0 )
{
fprintf(_pfDsc, "\n\t%s is %g;",
SzcTokenMap(tokenVersion),
RVersion() );
}
if ( ZsCreator().length() > 0 )
{
fprintf(_pfDsc, "\n\t%s is \"%s\";",
SzcTokenMap(tokenCreator),
ZsCreator().Szc() );
}
if ( ZsFormat().length() > 0 )
{
fprintf(_pfDsc, "\n\t%s is \"%s\";",
SzcTokenMap(tokenFormat),
ZsFormat().Szc() );
}
fprintf( _pfDsc, "\n}\n\n" );
}
//
// Regenerate the property type declarations.
//
// If any are marked "standard", generate the "import standard" declaration.
// Generate explicit "import" declarations for any marked "persistent".
//
void MBNETDSC :: PrintPropertyDeclarations()
{
int cTypes = 0;
MBNET::ITER mbnit( self, GOBJMBN::EBNO_PROP_TYPE );
GOBJMBN * pgmobj;
bool bImportStandard = false;
for ( ; pgmobj = *mbnit ; ++mbnit)
{
ZSREF zsrName = mbnit.ZsrCurrent();
GOBJPROPTYPE * pbnpt;
DynCastThrow( pgmobj, pbnpt );
if ( cTypes++ == 0 )
{
fprintf( _pfDsc, "%s\n{",
SzcTokenMap(tokenProperties) );
}
assert( zsrName == pbnpt->ZsrefName() );
// If this is a standard persistent property,
// write the import declaration once
if ( pbnpt->FPropType() & fPropStandard )
{
if ( ! bImportStandard )
{
// Write the "import" statement once
fprintf( _pfDsc, "\n\timport standard;" );
bImportStandard = true;
}
// Skip further processing of standard imported types
continue;
}
// If this is a persistent property, write the import declaration
if ( pbnpt->FPropType() & fPropPersist )
{
fprintf( _pfDsc, "\n\timport %s;", zsrName.Szc() );
continue;
}
// User-declared (private, non-persistent) property
fprintf( _pfDsc, "\n\ttype %s = ", zsrName.Szc() );
if ( pbnpt->FPropType() & fPropArray )
{
fprintf( _pfDsc, "%s %s ",
SzcTokenMap(tokenArray),
SzcTokenMap(tokenOf) );
}
if ( pbnpt->FPropType() & fPropChoice )
{
fprintf( _pfDsc, "%s %s \n\t\t[",
SzcTokenMap(tokenWordChoice),
SzcTokenMap(tokenOf) );
int cc = pbnpt->VzsrChoice().size();
for ( int ic = 0; ic < cc; ic++ )
{
fprintf( _pfDsc, "%s", pbnpt->VzsrChoice()[ic].Szc() );
if ( ic+1 < cc )
fprintf( _pfDsc, "," );
}
fprintf( _pfDsc, "]" );
}
else
if ( pbnpt->FPropType() & fPropString )
{
fprintf( _pfDsc, "%s", SzcTokenMap(tokenWordString) );
}
else
{
fprintf( _pfDsc, "%s", SzcTokenMap(tokenWordReal) );
}
if ( pbnpt->ZsrComment().Zstr().length() > 0 )
{
fprintf( _pfDsc, ",\n\t\t\"%s\"",
pbnpt->ZsrComment().Szc() );
}
fprintf( _pfDsc, ";" );
}
if ( cTypes )
{
PrintPropertyList( LtProp() );
fprintf( _pfDsc, "\n}\n" );
}
}
void MBNETDSC :: PrintNodes()
{
MBNET::ITER mbnit( self, GOBJMBN::EBNO_NODE );
GOBJMBN * pgmobj;
GNODEMBN * pbnode;
GNODEMBND * pbnoded;
for ( ; pgmobj = *mbnit ; ++mbnit)
{
ZSREF zsrName = mbnit.ZsrCurrent();
DynCastThrow( pgmobj, pbnode );
assert( zsrName == pbnode->ZsrefName() );
fprintf( _pfDsc, "\n%s %s\n{",
SzcTokenMap(tokenNode),
pbnode->ZsrefName().Szc() );
if ( pbnode->ZsFullName().length() > 0 )
{
fprintf( _pfDsc, "\n\t%s = \"%s\";",
SzcTokenMap(tokenName),
pbnode->ZsFullName().Szc() );
}
pbnoded = dynamic_cast<GNODEMBND *>(pbnode);
ASSERT_THROW( pbnoded, EC_NYI, "only discrete nodes supported" )
// Print the type and states using a domain, if given
if ( pbnoded->ZsrDomain().Zstr().length() > 0 )
{
// Explicit domain
fprintf( _pfDsc, "\n\t%s = %s %s %s;",
SzcTokenMap(tokenType),
SzcTokenMap(tokenDiscrete),
SzcTokenMap(tokenDomain),
pbnoded->ZsrDomain().Szc() );
}
else
{
// Variable-specific state enumeration
int cState = pbnoded->CState();
fprintf( _pfDsc, "\n\t%s = %s[%d]\n\t{",
SzcTokenMap(tokenType),
SzcTokenMap(tokenDiscrete),
cState );
for ( int iState = 0; iState < cState; )
{
fprintf(_pfDsc, "\n\t\t\"%s\"",
pbnoded->VzsrStates()[iState].Szc() );
if ( ++iState < cState )
fprintf( _pfDsc, "," );
}
fprintf( _pfDsc, "\n\t};\n" );
}
PTPOS pt = pbnode->PtPos();
if ( pt._x != 0 || pt._y != 0 )
{
fprintf( _pfDsc, "\n\t%s = (%d, %d);",
SzcTokenMap(tokenPosition),
pt._x,
pt._y );
}
PrintPropertyList( pbnode->LtProp() );
fprintf( _pfDsc, "\n}\n");
}
}
void MBNETDSC :: PrintPropertyList ( LTBNPROP & ltProp )
{
for ( LTBNPROP::iterator ltit = ltProp.begin();
ltit != ltProp.end();
++ltit )
{
const PROPMBN & prop = *ltit;
fprintf( _pfDsc, "\n\t%s = ",
prop.ZsrPropType().Szc() );
bool bArray = prop.FPropType() & fPropArray;
if ( bArray )
fprintf( _pfDsc, "[" );
for ( int i = 0; i < prop.Count(); )
{
if ( prop.FPropType() & fPropChoice )
{
GOBJMBN * pgmobj = Mpsymtbl().find( prop.ZsrPropType() );
assert( pgmobj );
GOBJPROPTYPE * pbnpt;
DynCastThrow( pgmobj, pbnpt );
fprintf( _pfDsc, "%s",
pbnpt->VzsrChoice()[(int) prop.Real(i)].Szc() );
}
else
if ( prop.FPropType() & fPropString )
{
fprintf( _pfDsc, "\"%s\"",
prop.Zsr(i).Szc() );
}
else
{
fprintf( _pfDsc, "%g",
prop.Real(i) );
}
if ( ++i < prop.Count() )
fprintf( _pfDsc, "," );
}
if ( bArray )
fprintf( _pfDsc, "]" );
fprintf( _pfDsc, ";" );
}
}
//
// Print network topology and probability distribution information for
// all nodes.
//
// Note that distributions are stored in the distribution map
// most of the time. However, during network expansion and inference
// they are temporarly bound to their respective nodes (see 'BindDistributions').
// For purposes of dumping the network at various stages, this logic
// will print a bound distribution in preference to a mapped one.
// If no distribution can be found, an error is generated as a comment into
// the output file.
//
void MBNETDSC :: PrintTopologyAndDistributions()
{
MBNET::ITER mbnit( self, GOBJMBN::EBNO_NODE );
GOBJMBN * pgmobj;
VTKNPD vtknpd;
for ( ; pgmobj = *mbnit ; ++mbnit)
{
ZSREF zsrName = mbnit.ZsrCurrent();
GNODEMBN * pbnode;
DynCastThrow( pgmobj, pbnode );
pbnode->GetVtknpd( vtknpd );
GNODEMBND * pbnoded = dynamic_cast<GNODEMBND *>(pbnode);
if ( pbnoded == NULL )
{
// We don't have a clue as to how to print this node
fprintf( _pfDsc,
"\n\n// Error: unable to print distribution for non-discrete node \'%s\'",
zsrName.Szc() );
continue;
}
if ( pbnoded->BHasDist() )
{
// This node already has a bound distribution
// Construct the token array describing the distribution
ZSTR zsSig = vtknpd.ZstrSignature(1);
fprintf( _pfDsc, "\n%s(%s)\t\n{",
SzcTokenMap(tokenProbability),
zsSig.Szc() );
PrintDistribution( *pbnoded, pbnoded->Bndist() );
fprintf( _pfDsc,"\n}\n");
continue;
}
// Look the distribution up in the map
// Cons-up "p(<node>|"
VTKNPD vtknpdNode;
vtknpdNode.push_back( TKNPD(DTKN_PD) );
vtknpdNode.push_back( TKNPD( pbnode->ZsrefName() ) );
// Find the distribution(s) with that signature; print the first one
int cFound = 0;
for ( MPPD::iterator mppdit = Mppd().lower_bound( vtknpdNode );
mppdit != Mppd().end();
++mppdit )
{
const VTKNPD & vtknpdMap = (*mppdit).first;
if ( vtknpdMap.size() < 2
|| vtknpdMap[0] != TKNPD(DTKN_PD)
|| ! vtknpdMap[1].BStr() )
break;
SZC szcNode = vtknpdMap[1].Szc();
if ( pbnode->ZsrefName().Szc() != szcNode )
break;
if ( cFound++ == 0 )
{
ZSTR zsTopol = vtknpdMap.ZstrSignature(1);
fprintf( _pfDsc, "\n%s(%s)\t\n{",
SzcTokenMap(tokenProbability),
zsTopol.Szc() );
if ( vtknpd != vtknpdMap )
{
ZSTR zsSig = vtknpd.ZstrSignature();
fprintf( _pfDsc,
"\n\n\t// Error: required distribution is %s",
zsSig.Szc() );
}
PrintDistribution( *pbnode, *(*mppdit).second );
fprintf( _pfDsc,"\n}\n");
}
else
{
ZSTR zsSig = vtknpd.ZstrSignature();
fprintf( _pfDsc,
"\n\n// Warning: Superfluous distribution found for %s",
zsSig.Szc() );
}
}
if ( cFound > 0 )
continue;
// Print a warning into the DSC output file
ZSTR zsSigFull = vtknpd.ZstrSignature();
fprintf( _pfDsc,
"\n\n// Error: Distribution missing for %s",
zsSigFull.Szc() );
// Construct the token array describing the distribution, but write
// it as empty.
ZSTR zsSig = vtknpd.ZstrSignature(1);
fprintf( _pfDsc, "\n%s(%s);",
SzcTokenMap(tokenProbability),
zsSig.Szc() );
}
fflush( _pfDsc );
}
void MBNETDSC :: PrintDistribution ( GNODEMBN & gnode, BNDIST & bndist )
{
BNDIST::EDIST edist = bndist.Edist();
switch ( edist )
{
case BNDIST::ED_CI_MAX:
case BNDIST::ED_CI_PLUS:
{
SZC szcFunc = SzcDist( edist );
assert( szcFunc );
fprintf( _pfDsc, "\n\tfunction = %s;", szcFunc );
// Fall through to handle as sparse
}
case BNDIST::ED_SPARSE:
{
const MPCPDD & dmp = bndist.Mpcpdd();
int cEntries = dmp.size();
for ( MPCPDD::const_iterator dmit = dmp.begin();
dmit != dmp.end();
++dmit)
{
const VIMD & vimd = (*dmit).first;
const VLREAL & vr = (*dmit).second;
fprintf( _pfDsc, "\n\t");
if ( vimd.size() == 0 )
{
if ( cEntries > 1 )
fprintf( _pfDsc, "%s = ", SzcTokenMap(tokenDefault) );
}
else
{
fprintf( _pfDsc, "(");
for ( int i = 0; i < vimd.size() ; )
{
fprintf( _pfDsc, "%d", vimd[i] );
if ( ++i < vimd.size() )
fprintf( _pfDsc, ", " );
}
fprintf( _pfDsc, ") = ");
}
for ( int ir = 0; ir < vr.size(); )
{
fprintf( _pfDsc, "%g", vr[ir] );
if ( ++ir < vr.size() )
fprintf( _pfDsc, ", " );
}
fprintf( _pfDsc, ";" );
}
break;
}
case BNDIST::ED_DENSE:
{
MDVCPD mdv = bndist.Mdvcpd();
MDVCPD::Iterator itdd( mdv );
int cDim = mdv.VimdDim().size();
int cStates = mdv.VimdDim()[cDim -1];
for ( int iState = 0; itdd.BNext(); iState++ )
{
const VIMD & vimd = itdd.Vitmd();
if ( (iState % cStates) == 0 )
{
// Start a new row
fprintf( _pfDsc, "\n\t" );
// Prefix with parent instantations if necessary
int cItems = vimd.size() - 1;
if ( cItems )
{
fprintf( _pfDsc, "(" );
for ( int i = 0; i < cItems ; )
{
fprintf( _pfDsc, "%d", vimd[i] );
if ( ++i < cItems )
fprintf( _pfDsc, ", " );
}
fprintf( _pfDsc, ") = ");
}
}
REAL & r = itdd.Next();
fprintf( _pfDsc, "%g%c ", r, ((iState+1) % cStates) ? ',' : ';' );
}
break;
}
default:
THROW_ASSERT(EC_NYI, "PrintDistribution only implemented for sparse arrays");
break;
}
}