|
|
//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1997 - 1998
//
// File: gnodembn.cpp
//
//--------------------------------------------------------------------------
//
// GNODEMBN.CPP
//
#include <basetsd.h>
#include <typeinfo.h>
#include "gmobj.h"
#include "cliqset.h"
#include "clique.h"
#include "algos.h"
#include "domain.h"
/*****************************************************************************************
Cloning and cloning functions.
There are two types of cloning member functions:
1) functions named "Clone", in which the new object is being asked to initialize itself from another existing object. This is not a copy constructor due to the complexity of timing chained construcions.
2) functions named "CloneNew", in which an existing object is being asked to participate in the construction of a new object.
Functions of type #1 are straightforward, such as
virtual void MODEL :: Clone ( MODEL & model );
Here, a MODEL is being asked to clone or copy information from an existing network into itself. The object can place whatever restrictions it would like on such functions. For example, the MODEL class requires that the new MODEL object be entirely empty.
Functions of type #2 are more complex, such as
virtual GOBJMBN * GOBJMBN :: CloneNew ( MBNET & mbnetSelf, MBNET & mbnetNew, GOBJMBN * pgobjNew = NULL );
In this case, there are references to the original and clone networks (MBNETs), and a pointer to the newly constructed object, which may be NULL. Consider a chain of inheritance such as: class OBJ; class SUB_OBJ : public OBJ; class SUB_SUB_OBJ : public SUB_OBJ;
If a new SUB_SUB_OBJ is to be cloned from an existing one, an empty object must be constructed first. Then the CloneNew() function the original SUB_SUB_OBJ is called. At this point, there's a choice: does the SUB_SUB_OBJ perform all the initialization for all base classes, or should it defer data member cloning to its base classes? We use the latter approach, as C++ itself does for construction and destruction.
So, at the top level of object cloning, the initial invocation of CloneNew() will usually have a NULL object pointer. Each CloneNew() function must check for this, and either create a new object, if allowed, or throw an exception. It will then call the CloneNew() function for its immediate ancestral base class using the new pointer. The ancestral CloneNew() function will see that there already is a pointer and simply use it. In other words, the Clone() member functions are simple "build yourself from another" commands. The CloneNew() functions collaborate with all ancestral base classes to correctly construct an object with interdependencies. Note that the semantics (source vs. target) are reversed.
The bulk of the complexity in cloning an MBNET or MODEL (or subclass) arises from the internal string symbol table and the storage of references to strings throughout the set of associated objects. *****************************************************************************************/
// MSRDEVBUG: This should not be required since it's pure virtual, but VC++ 5.0 gets confused.
GOBJMBN :: ~ GOBJMBN () { }
GOBJMBN * GOBJMBN :: CloneNew ( MODEL & modelSelf, MODEL & modelNew, GOBJMBN * pgobjNew ) { // If we're expected to create the object, that's a no-no; throw an exception
if ( pgobjNew == NULL ) { ThrowInvalidClone( self ); }
// Update class-specific member variables
pgobjNew->IMark() = IMark(); pgobjNew->IType() = IType();
// Convert and assign the name, if any
if ( ZsrefName()->length() > 0 ) { pgobjNew->SetName( modelNew.Mpsymtbl().intern( ZsrefName().Szc() ) ) ; } // Handle other variables
pgobjNew->_vFlags = _vFlags; return pgobjNew; }
GNODEMBN :: GNODEMBN() :_iTopLevel(-1) { IType() = 0; }
GNODEMBN :: ~ GNODEMBN() { }
GOBJMBN * GNODEMBN :: CloneNew ( MODEL & modelSelf, MODEL & modelNew, GOBJMBN * pgobjNew )
{ GNODEMBN * pgnd = NULL; if ( pgobjNew == NULL ) { pgnd = new GNODEMBN; } else { DynCastThrow( pgobjNew, pgnd ); } ASSERT_THROW( GOBJMBN::CloneNew( modelSelf, modelNew, pgnd ), EC_INTERNAL_ERROR, "cloning failed to returned object pointer" );
// Update class-specific member variables
pgnd->_iTopLevel = _iTopLevel; pgnd->_ltProp.Clone( modelNew, modelSelf, _ltProp ); pgnd->_ptPos = _ptPos; pgnd->_zsFullName = _zsFullName; pgnd->_clampIface = _clampIface;
return pgnd; }
GOBJMBN * GNODEMBND :: CloneNew ( MODEL & modelSelf, MODEL & modelNew, GOBJMBN * pgobjNew ) { GNODEMBND * pgndd = NULL; if ( pgobjNew == NULL ) { pgndd = new GNODEMBND; } else { DynCastThrow( pgobjNew, pgndd ); } ASSERT_THROW( GNODEMBN::CloneNew( modelSelf, modelNew, pgndd ), EC_INTERNAL_ERROR, "cloning failed to returned object pointer" );
// Update class-specific member variables
modelNew.Mpsymtbl().CloneVzsref( modelSelf.Mpsymtbl(), _vzsrState, pgndd->_vzsrState ); return pgndd; }
void GNODEMBN :: Visit ( bool bUpwards /* = true */ ) { if ( IMark() ) return;
INT iMarkMax = 0; GNODENUM<GNODEMBN> benum( bUpwards ); benum.SetETypeFollow( GEDGEMBN::ETPROB );
for ( benum.Set( this ); benum.PnodeCurrent(); benum++ ) { GNODEMBN * pgndbn = *benum; pgndbn->Visit( bUpwards ); if ( pgndbn->IMark() > iMarkMax ) iMarkMax = pgndbn->IMark(); } IMark() = iMarkMax + 1; }
//
// Fill array with parent pointers (follow directed arcs)
//
// About network "expansion". When CI expansion occurs, nodes
// affected are marked with the flag "EIBF_Expanded". This routine
// normally does one of two things:
//
// If the node is expanded, only parents marked as "EIBF_Expansion"
// are considered as real parents.
//
// If the node is not marked, only parents which are not marked as
// "expansion" are considered.
//
// This can be overridden with the "bUseExpansion" flag, in which case
// the original (pre-expansion) parents will be delivered.
//
void GNODEMBN :: GetParents ( VPGNODEMBN & vpgnode, // Result array
bool bIncludeSelf, // If true, place self as last entry in list
bool bUseExpansion ) // If true, consider expansion information
{ // If requested, and if this node is part of network expansion, only
// consider expansion parents. Otherwise, ignore them and only use real
// parents.
bool bOnlyUseExpansionParents = bUseExpansion && BFlag( EIBF_Expanded ) ;
// Prepare to iterate over the parents
GNODENUM<GNODEMBN> benumparent(true); benumparent.SetETypeFollow( GEDGEMBN::ETPROB ); for ( benumparent.Set( this ); benumparent.PnodeCurrent(); benumparent++ ) { GNODEMBN * pgndParent = *benumparent; bool bExpansion = pgndParent->BFlag( EIBF_Expansion ); if ( bOnlyUseExpansionParents ^ bExpansion ) continue; vpgnode.push_back( pgndParent ); } if ( bIncludeSelf ) vpgnode.push_back( this ); }
// Return the discrete dimension vector of this node if possible;
// return false if any parent is not discrete.
bool GNODEMBND :: BGetVimd ( VIMD & vimd, // Array to fill
bool bIncludeSelf, // Place self as last entry in list
bool bUseExpansion ) // If expanded, use expansion only
{ // Get the parents according to the flags
VPGNODEMBN vpgndParents; GetParents( vpgndParents, bIncludeSelf, bUseExpansion ); // Prepare the result array
vimd.resize( vpgndParents.size() ); for ( int i = 0; i < vimd.size(); i++ ) { // See if the next node is discrete; return false if not
GNODEMBND * pgnddParent = dynamic_cast<GNODEMBND *> (vpgndParents[i]); if ( pgnddParent == NULL ) return false; // Add to the dimension array
assert( pgnddParent->IType() & FND_Discrete ); vimd[i] = pgnddParent->CState(); } return true; }
// Fill array with child pointers (follow directed arcs)
void GNODEMBN :: GetChildren ( VPGNODEMBN & vpgnode, bool bIncludeSelf ) { // Prepare to iterate over the children
GNODENUM<GNODEMBN> benumchild(false); benumchild.SetETypeFollow( GEDGEMBN::ETPROB ); for ( benumchild.Set( this ); benumchild.PnodeCurrent(); benumchild++ ) { vpgnode.push_back( *benumchild ); } if ( bIncludeSelf ) vpgnode.push_back( this ); }
// Fill array with neighbors (follow undirected arcs)
void GNODEMBN :: GetNeighbors ( VPGNODEMBN & vpgnode, bool bIncludeSelf ) { // Iterate over all connections to the source node.
// That is, arcs in either direction.
GNODENUM_UNDIR gnenumUndir; // Initialize the iterator
for ( gnenumUndir = this; gnenumUndir.PnodeCurrent(); gnenumUndir++ ) { vpgnode.push_back( *gnenumUndir ); } if ( bIncludeSelf ) vpgnode.push_back( this ); }
int GNODEMBN :: IParent ( GNODEMBN * pgndmb, bool bReverse ) { // Prepare to iterate over the parents
GNODENUM<GNODEMBN> benumparent( true, ! bReverse ); benumparent.SetETypeFollow( GEDGEMBN::ETPROB ); int iParent = 0; for ( benumparent.Set(this); benumparent.PnodeCurrent(); benumparent++, iParent++ ) { if ( *benumparent == pgndmb ) return iParent; } return -1; }
int GNODEMBN :: IChild ( GNODEMBN * pgndmb, bool bReverse ) { // Prepare to iterate over the children
GNODENUM<GNODEMBN> benumchild( false, ! bReverse ); benumchild.SetETypeFollow( GEDGEMBN::ETPROB ); int iChild = 0; for ( benumchild.Set(this); benumchild.PnodeCurrent(); benumchild++ ) { if ( *benumchild == pgndmb ) return iChild; } return -1; }
bool GNODEMBN :: BIsNeighbor ( GNODEMBN * pgndmb ) { GNODENUM_UNDIR gnenumUndir; for ( gnenumUndir = this; gnenumUndir.PnodeCurrent(); gnenumUndir++ ) { if ( *gnenumUndir == pgndmb ) return true; } return false; }
void GNODEMBN :: GetVtknpd ( VTKNPD & vtknpd, bool bUseExpansion ) { VPGNODEMBN vpgnodeParent; GetParents(vpgnodeParent, false, bUseExpansion);
vtknpd.clear(); vtknpd.push_back( TKNPD(DTKN_PD) ); vtknpd.push_back( TKNPD( ZsrefName() ) );
for ( int ip = 0; ip < vpgnodeParent.size(); ip++ ) { if ( ip > 0 ) vtknpd.push_back( TKNPD(DTKN_AND) ); else vtknpd.push_back( TKNPD(DTKN_COND) ); vtknpd.push_back( TKNPD(vpgnodeParent[ip]->ZsrefName()) ); } }
bool GNODEMBN :: BMatchTopology ( MBNET & mbnet, const VTKNPD & vtknpd, VPGNODEMBN * pvpgnode ) { // Guarantee that the descriptor is of the form "p(X|...)"
if ( vtknpd.size() < 2 || vtknpd[0] != TKNPD(DTKN_PD) || ! vtknpd[1].BStr() ) throw GMException( EC_INV_PD, "invalid token description on PD");
VTKNPD vtknpdSelf; GetVtknpd( vtknpdSelf );
if ( vtknpdSelf == vtknpd ) return true;
#ifdef _DEBUG
{ ZSTR zs1 = vtknpd.ZstrSignature(0); ZSTR zs2 = vtknpdSelf.ZstrSignature(0); cout << "\nGNODEMBN::BMatchTopology mismatch: " << "\n\tExpected " << zs1 << "\n\tComputed " << zs2 ; } #endif
return false; }
void GNODEMBN :: Dump () { cout << "\t(toplev: " << ITopLevel() << "): " << ZsrefName().Szc();
int iParent = 0; GNODENUM<GNODEMBN> benumparent(true); benumparent.SetETypeFollow( GEDGEMBN::ETPROB );
for ( benumparent.Set(this); benumparent.PnodeCurrent(); benumparent++ ) { GNODEMBN * pgndbnParent = *benumparent; if ( iParent++ == 0 ) cout << ", parents: "; cout << pgndbnParent->ZsrefName().Szc() << ','; } }
GNODEMBND :: GNODEMBND () { IType() = FND_Valid | FND_Discrete ; }
GNODEMBND :: ~ GNODEMBND () { ClearDist(); }
void GNODEMBND :: Dump () { GNODEMBN::Dump(); if ( BHasDist() && Bndist().Edist() != BNDIST::ED_NONE ) { cout << "\n\tprobability distribution of " << ZsrefName().Szc() << ": "; Bndist().Dump(); } }
// Find the distribution for this node recorded in the belief network's
// distribution map.
void GNODEMBND :: SetDist ( MBNET & mbnet ) { ClearDist(); // Construct the token array describing the distribution
VTKNPD vtknpd; GetVtknpd( vtknpd ); // Locate that distribution in the belief network's map
MPPD::iterator itmppd = mbnet.Mppd().find( vtknpd ); ASSERT_THROW( itmppd != mbnet.Mppd().end(), EC_INTERNAL_ERROR, "missing distribution for node" ); // Set this node to use that distribution
_refbndist = (*itmppd).second; assert( BHasDist() ); }
// Bind the given distribution this node
void GNODEMBND :: SetDist ( BNDIST * pbndist ) { #ifdef _DEBUG
if ( pbndist ) { // Check that the last dimension is the correct size.
int cDims = pbndist->VimdDim().size(); assert( pbndist->VimdDim()[cDims-1] == CState() ); } #endif
_refbndist = pbndist; }
// Check that the dimensionality of the distribution matches that of
// the node itself according to the dag topology.
bool GNODEMBND :: BCheckDistDense () { // Get the array of parents
VPGNODEMBN vpgndParents; GetParents( vpgndParents ); VIMD vimd( vpgndParents.size() + 1 ); for ( int idim = 0; idim < vimd.size() - 1; idim++ ) { GNODEMBND * pgndd; assert( vpgndParents[idim] ); DynCastThrow( vpgndParents[idim], pgndd ); vimd[idim] = pgndd->CState(); } vimd[idim] = CState(); MDVCPD & mdv = Bndist().Mdvcpd(); return mdv.VimdDim() == vimd; }
void GNODEMBND :: SetDomain ( const GOBJMBN_DOMAIN & gobjrdom ) { // Copy the state names from the domain to the variable
const RDOMAIN & rdom = gobjrdom.Domain(); RDOMAIN::const_iterator itdm = rdom.begin(); _vzsrState.resize( rdom.size() ); for ( int i = 0; itdm != rdom.end(); itdm++ ) { const RANGEDEF & rdef = *itdm; _vzsrState[i++] = rdef.ZsrName(); } _zsrDomain = gobjrdom.ZsrefName(); }
//
// Usage of this function without a new object implies that the
// subclassed target object does not correctly support "CloneNew".
// Throw a cloning exception in this case.
//
GEDGEMBN * GEDGEMBN :: CloneNew ( MODEL & modelSelf, MODEL & modelNew, GOBJMBN * pgobjmbnSource, GOBJMBN * pgobjmbnSink, GEDGEMBN * pgedgeNew ) { if ( pgedgeNew == NULL ) { ThrowInvalidClone( self ); } pgedgeNew->_vFlags = _vFlags; return pgedgeNew; }
GEDGEMBN * GEDGEMBN_PROB :: CloneNew ( MODEL & modelSelf, MODEL & modelNew, GOBJMBN * pgobjmbnSource, GOBJMBN * pgobjmbnSink, GEDGEMBN * pgdegeNew ) { assert( EType() == ETPROB ); GNODEMBN * pgndSource; GNODEMBN * pgndSink;
DynCastThrow( pgobjmbnSource, pgndSource ); DynCastThrow( pgobjmbnSink, pgndSink );
GEDGEMBN_PROB * pgedge = new GEDGEMBN_PROB( pgndSource, pgndSink ); ASSERT_THROW( GEDGEMBN::CloneNew( modelSelf, modelNew, pgndSource, pgndSink, pgedge ), EC_INTERNAL_ERROR, "cloning failed to returned object pointer" ); return pgedge; }
bool BNWALKER :: BSelect ( GNODEMBN * pgn ) { return true; }
bool BNWALKER :: BMark ( GNODEMBN * pgn ) { return true; }
|