1426 lines
36 KiB
C++
1426 lines
36 KiB
C++
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 1997 - 1998
|
|
//
|
|
// File: recomend.cpp
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
//
|
|
// recomend.cpp: Fix-or-repair planning recommendations
|
|
//
|
|
|
|
#include <basetsd.h>
|
|
#include <math.h>
|
|
#include <float.h>
|
|
#include "algos.h"
|
|
#include "recomend.h"
|
|
#include "parmio.h"
|
|
|
|
#ifdef _DEBUG
|
|
//#define DUMP // Uncomment for copious diagnostic output
|
|
#endif
|
|
|
|
const PROB probTiny = 1e-6; // General probability tolerance
|
|
|
|
static
|
|
ostream & operator << ( ostream & os, GPNDDDIST & gpnddist )
|
|
{
|
|
os << "GPNDDDIST: ";
|
|
if ( gpnddist.Pgnd() )
|
|
{
|
|
os << gpnddist.Pgnd()->ZsrefName().Szc()
|
|
<< ", distribution: "
|
|
<< gpnddist.Dist();
|
|
}
|
|
else
|
|
{
|
|
os << "<NULL>";
|
|
}
|
|
return os;
|
|
}
|
|
|
|
static
|
|
ostream & operator << ( ostream & os, GNODEREFP & gndref )
|
|
{
|
|
assert( gndref.Pgndd() );
|
|
os << "GNODEREFP: "
|
|
<< gndref.Gndd().ZsrefName().Szc()
|
|
<< " (obs = "
|
|
<< gndref.CostObserve()
|
|
<< ", fix = "
|
|
<< gndref.CostFix()
|
|
<< ", util = "
|
|
<< gndref.Util()
|
|
<< ", lbl = "
|
|
<< PROPMGR::SzcLbl( gndref.ELbl() )
|
|
<< ")";
|
|
return os;
|
|
}
|
|
|
|
static
|
|
ostream & operator << ( ostream & os, GNODERECWORK & gnrw )
|
|
{
|
|
os << "GNODERECWORK: "
|
|
<< gnrw.Gndref()
|
|
<< ", p/c = "
|
|
<< gnrw.PbOverCost()
|
|
<< ", p(fault) = "
|
|
<< gnrw.PbFault();
|
|
return os;
|
|
}
|
|
|
|
static
|
|
inline
|
|
bool BIsUnity( const REAL & r )
|
|
{
|
|
return 1.0 - probTiny < r && r < 1.0 + probTiny;
|
|
}
|
|
|
|
static
|
|
inline
|
|
bool BEqual ( const REAL & ra, const REAL & rb )
|
|
{
|
|
//return fabs( ra - rb ) <= probTiny;
|
|
return ra != 0.0
|
|
? BIsUnity( rb / ra )
|
|
: rb == 0.0;
|
|
}
|
|
|
|
//
|
|
// Ordering routines for arrays of GNODERECWORKs
|
|
//
|
|
typedef binary_function<const GNODERECWORK &, const GNODERECWORK &, bool> SORTGNODERECWORK;
|
|
|
|
// The greater the prob-over-cost, the lower the sort order
|
|
class SRTGNW_SgnProbOverCost : public SORTGNODERECWORK
|
|
{
|
|
public:
|
|
bool operator () (const GNODERECWORK & gnwa, const GNODERECWORK & gnwb) const
|
|
{
|
|
PROB pra = gnwa.PbOverCost();
|
|
PROB prb = gnwb.PbOverCost();
|
|
return pra > prb;
|
|
}
|
|
};
|
|
|
|
// The greater the prob fault, the lower the sort order
|
|
class SRTGNW_SgnProb : public SORTGNODERECWORK
|
|
{
|
|
public:
|
|
bool operator () (const GNODERECWORK & gnwa, const GNODERECWORK & gnwb) const
|
|
{
|
|
// Force leak terms to sort high
|
|
int iLeak = 0;
|
|
if ( ! gnwa->BLeak() && gnwb->BLeak() )
|
|
iLeak = -1; // Unleak < leak
|
|
else
|
|
if ( gnwa->BLeak() && ! gnwb->BLeak() )
|
|
iLeak = 1; // Leak > Unleak
|
|
if ( iLeak != 0 )
|
|
return iLeak;
|
|
|
|
PROB pra = gnwa.PbFault();
|
|
PROB prb = gnwb.PbFault();
|
|
return pra > prb;
|
|
}
|
|
};
|
|
|
|
// The lower the cost-to-observe, the lower the sort order
|
|
class SRTGNW_SgnNegCost : public SORTGNODERECWORK
|
|
{
|
|
public:
|
|
bool operator () (const GNODERECWORK & gnwa, const GNODERECWORK & gnwb) const
|
|
{
|
|
COST costa = gnwa.CostObsIfFixable();
|
|
COST costb = gnwb.CostObsIfFixable();
|
|
return costa < costb;
|
|
}
|
|
};
|
|
|
|
// The higher the utility, the lower the sort order
|
|
class SRTGNW_SgnUtil : public SORTGNODERECWORK
|
|
{
|
|
public:
|
|
bool operator () (const GNODERECWORK & gnwa, const GNODERECWORK & gnwb) const
|
|
{
|
|
COST utila = gnwa.Gndref().Util();
|
|
COST utilb = gnwb.Gndref().Util();
|
|
return utila > utilb;
|
|
}
|
|
};
|
|
|
|
|
|
//
|
|
// Construct a node reference object. Extract properties, etc.
|
|
//
|
|
GNODEREFP :: GNODEREFP ( PROPMGR & propMgr, GNODEMBND * pgndd )
|
|
:_pgndd(pgndd),
|
|
_costObserve(0.0),
|
|
_costFix(0.0),
|
|
_costUtil(0.0),
|
|
_eLbl(ESTDLBL_other)
|
|
{
|
|
ASSERT_THROW( pgndd, EC_NULLP, "invalid GNOEREFP construction" );
|
|
|
|
PROPMBN * pprop = propMgr.PFind( *pgndd, ESTDP_cost_fix );
|
|
if ( pprop )
|
|
_costFix = pprop->Real();
|
|
pprop = propMgr.PFind( *pgndd, ESTDP_cost_observe );
|
|
if ( pprop )
|
|
_costObserve = pprop->Real();
|
|
pprop = propMgr.PFind( *pgndd, ESTDP_label );
|
|
if ( pprop )
|
|
_eLbl = (ESTDLBL) propMgr.IUserToLbl( pprop->Real() );
|
|
_bLeak = pgndd->BFlag( EIBF_Leak );
|
|
|
|
// If it's unobservable, use cost-to-fix as cost-to-observe
|
|
if ( _eLbl == ESTDLBL_fixunobs && _costObserve == 0.0 )
|
|
{
|
|
_costObserve = _costFix;
|
|
_costFix = 0.0;
|
|
}
|
|
}
|
|
|
|
// Initialize a work record from a node reference object and its fault probability
|
|
void GNODERECWORK :: Init ( GNODEREFP * pgndref, PROB pbFault )
|
|
{
|
|
_pgndref = pgndref;
|
|
_pbFault = pbFault;
|
|
_pbOverCost = 0.0;
|
|
if ( BFixable() )
|
|
{
|
|
COST costObserve = _pgndref->CostObserve();
|
|
if ( costObserve != 0.0 )
|
|
_pbOverCost = _pbFault / costObserve;
|
|
assert( _finite( _pbOverCost ) );
|
|
}
|
|
}
|
|
|
|
// Initialize a work record from a node reference object
|
|
void GNODERECWORK :: Init ( MBNET_RECOMMENDER & mbnRecom, GNODEREFP * pgndref )
|
|
{
|
|
MDVCPD mdv;
|
|
_pgndref = pgndref;
|
|
mbnRecom.InferGetBelief( _pgndref->Pgndd(), mdv );
|
|
Init( pgndref, 1.0 - mdv[0] );
|
|
}
|
|
|
|
void VGNODERECWORK :: InitElem ( GNODEREFP * pgndref, int index /* = -1 */ )
|
|
{
|
|
// Grow the array as needed
|
|
if ( index < 0 )
|
|
index = size();
|
|
if ( index >= size() )
|
|
resize( index+1 );
|
|
|
|
// Initialize the element
|
|
self[index].Init( MbnRec(), pgndref );
|
|
}
|
|
|
|
void VGNODERECWORK :: InitElem ( GNODEMBND * pgndd, int index )
|
|
{
|
|
// Find the node reference record in the recommendations object's array
|
|
VPGNODEREFP & vpgndref = MbnRec().Vpgndref();
|
|
int indref = vpgndref.ifind( pgndd );
|
|
ASSERT_THROW( indref >= 0,
|
|
EC_INTERNAL_ERROR,
|
|
"node ref not found during recommendations" );
|
|
|
|
// Initialize using that reference
|
|
InitElem( vpgndref[indref], index );
|
|
}
|
|
|
|
|
|
COST VGNODERECWORK :: CostService () const
|
|
{
|
|
return MbnRec().CostService();
|
|
}
|
|
|
|
void VGNODERECWORK :: Sort ( ESORT esort )
|
|
{
|
|
iterator ibeg = begin();
|
|
iterator iend = end();
|
|
|
|
switch ( esort )
|
|
{
|
|
case ESRT_ProbOverCost:
|
|
{
|
|
sort( ibeg, iend, SRTGNW_SgnProbOverCost() );
|
|
break;
|
|
}
|
|
case ESRT_SgnProb:
|
|
{
|
|
sort( ibeg, iend, SRTGNW_SgnProb() );
|
|
break;
|
|
}
|
|
case ESRT_NegCost:
|
|
{
|
|
sort( ibeg, iend, SRTGNW_SgnNegCost() );
|
|
break;
|
|
}
|
|
case ESRT_SgnUtil:
|
|
{
|
|
sort( ibeg, iend, SRTGNW_SgnUtil() );
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
THROW_ASSERT( EC_INTERNAL_ERROR, "invalid sort selector in recommendations" );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// class INFOPLAN:
|
|
// Encloses an array of VGNODERECWORKs, each of which is a fix-and-repair
|
|
// sequence corresponding to a particular state of an informational node.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
class INFOPLAN
|
|
{
|
|
public:
|
|
INFOPLAN ( MBNET_RECOMMENDER & mbnRec, // The recommendations object
|
|
GNODEMBND & gndInfo, // The information node
|
|
VGNODERECWORK & vgndrwFixRepair ); // The existing f-r sequence
|
|
|
|
// Compute the cost of the sequence
|
|
COST Cost();
|
|
// Return true if all plans are equivalent
|
|
bool BSameSequence() { return _bSameSequence; };
|
|
|
|
protected:
|
|
MBNET_RECOMMENDER & _mbnRec; // The recommendations object
|
|
GNODEMBND & _gndInfo; // The info node represented
|
|
MDVCPD _dd; // Unconditional probability distribution
|
|
VVGNODERECWORK _vvgndrw; // Array of plan arrays
|
|
bool _bSameSequence; // True if all plans are equivalent
|
|
};
|
|
|
|
|
|
INFOPLAN :: INFOPLAN (
|
|
MBNET_RECOMMENDER & mbnRec,
|
|
GNODEMBND & gndInfo,
|
|
VGNODERECWORK & vgndrwFixRepair )
|
|
: _mbnRec(mbnRec),
|
|
_gndInfo(gndInfo),
|
|
_bSameSequence(false)
|
|
{
|
|
#ifdef DUMP
|
|
cout << "\nINFOPLAN::INFOPLAN: info node "
|
|
<< gndInfo.ZsrefName().Szc();
|
|
#endif
|
|
|
|
CLAMP clampInfo; // State of info node at call time
|
|
_mbnRec.InferGetEvidence( & _gndInfo, clampInfo );
|
|
assert( ! clampInfo.BActive() );
|
|
assert( _mbnRec.ELbl( _gndInfo ) == ESTDLBL_info );
|
|
|
|
// Get setup information
|
|
GNODEMBND * pgnddPDAbnormal = _mbnRec.PgnddProbDefAbnormal();
|
|
assert( pgnddPDAbnormal );
|
|
COST costService = _mbnRec.CostService();
|
|
|
|
// Get beliefs under this state of information
|
|
_mbnRec.InferGetBelief( & _gndInfo, _dd );
|
|
|
|
// Resize and initialize the array of fix/repair sequences
|
|
int cStates = _gndInfo.CState();
|
|
_vvgndrw.resize( cStates );
|
|
for ( int iplan = 0; iplan < cStates; iplan++ )
|
|
{
|
|
_vvgndrw[iplan].PmbnRec() = & _mbnRec;
|
|
}
|
|
_bSameSequence = true;
|
|
|
|
VGPNDDDIST vgndddFixRelevant; // Array of relevant fixable nodes
|
|
for ( iplan = 0; iplan < cStates; iplan++ )
|
|
{
|
|
// If this state is impossible, ignore it
|
|
PROB pbPlan = _dd[iplan];
|
|
if ( pbPlan == 0.0 )
|
|
continue;
|
|
|
|
#ifdef DUMP
|
|
cout << "\nINFOPLAN clamp "
|
|
<< gndInfo.ZsrefName().Szc()
|
|
<< " to state = "
|
|
<< iplan
|
|
<< ", prob = "
|
|
<< _dd[iplan];
|
|
#endif
|
|
|
|
// Clamp this info node to this state
|
|
CLAMP clamp( true, iplan, true );
|
|
_mbnRec.InferEnterEvidence( & _gndInfo, clamp );
|
|
|
|
// Determine which nodes are relevant given this state of information
|
|
_mbnRec.DetermineRelevantFixableNodes( vgndddFixRelevant, true, & _gndInfo );
|
|
|
|
// If there are no relevant fixables then the configuration is impossible
|
|
if ( vgndddFixRelevant.size() == 0 )
|
|
continue;
|
|
|
|
// Collect and sequence the relevant fixable nodes accordingly
|
|
_mbnRec.ComputeFixSequence( vgndddFixRelevant, _vvgndrw[iplan] );
|
|
|
|
// See if this is a new sequence
|
|
if ( _bSameSequence )
|
|
_bSameSequence = vgndrwFixRepair.BSameSequence( _vvgndrw[iplan] );
|
|
}
|
|
|
|
// Restore the info node to its entry state
|
|
_mbnRec.InferEnterEvidence( & _gndInfo, clampInfo );
|
|
|
|
#ifdef DUMP
|
|
cout << "\nINFOPLAN::INFOPLAN: END info node "
|
|
<< gndInfo.ZsrefName().Szc();
|
|
#endif
|
|
}
|
|
|
|
COST INFOPLAN :: Cost ()
|
|
{
|
|
VPGNODEREFP & vpgndref = _mbnRec.Vpgndref();
|
|
int indref = vpgndref.ifind( & _gndInfo );
|
|
assert( indref >= 0 );
|
|
COST cost = vpgndref[indref]->CostObserve();
|
|
ASSERT_THROW( cost != 0.0, EC_INTERNAL_ERROR, "missing observation cost for info node" );
|
|
|
|
// Rescale the probabilities of each planning state based upon removal of the
|
|
// impossible states and renormalization.
|
|
PROB pbTotal = 0.0;
|
|
for ( int iplan = 0; iplan < _gndInfo.CState(); iplan++ )
|
|
{
|
|
if ( _vvgndrw[iplan].size() > 0 )
|
|
pbTotal += _dd[iplan];
|
|
}
|
|
|
|
assert( pbTotal > 0.0 );
|
|
|
|
for ( iplan = 0; iplan < _gndInfo.CState(); iplan++ )
|
|
{
|
|
// Get the rescaled probability of this state of the info node
|
|
PROB pbPlan = _dd[iplan];
|
|
VGNODERECWORK & vgndrw = _vvgndrw[iplan];
|
|
if ( vgndrw.size() == 0 )
|
|
{
|
|
// The plan is zero length; in other words, no fixables were relevant
|
|
// and the plan is impossible
|
|
pbPlan = 0.0;
|
|
}
|
|
pbPlan /= pbTotal;
|
|
COST costPlan = _vvgndrw[iplan].Cost();
|
|
cost += costPlan * pbPlan;
|
|
}
|
|
return cost;
|
|
}
|
|
|
|
// Rescale the probabilities for the fix list. This routine sets the
|
|
// array bounds to ignore everything from the first unfixable node and beyond.
|
|
// Fault probabilities for the list are renormalized against the cumulative
|
|
// probability of all the faults in the array. Since there should be no fixable
|
|
// nodes of significance after the first unfixable node, the "probLeak" value
|
|
// should be very small.
|
|
void VGNODERECWORK :: Rescale ()
|
|
{
|
|
// Accumulate totals of all fault probabilities
|
|
PROB probTot = 0.0;
|
|
for ( int ind = 0; ind < size(); ind++ )
|
|
{
|
|
probTot += self[ind].PbFault();
|
|
}
|
|
|
|
PROB probLeak = 1.0; // Renormalized leak (residual) probability
|
|
int i1stUnfix = size(); // Index of 1st unfixable node
|
|
|
|
for ( ind = 0; ind < size(); ind++ )
|
|
{
|
|
GNODERECWORK & gndrw = self[ind];
|
|
|
|
if ( ! gndrw.BFixable() )
|
|
{
|
|
i1stUnfix = ind;
|
|
break;
|
|
}
|
|
|
|
//modified to fix the problem
|
|
//gndrw.SetPbFault( gndrw.PbFault()/probTot);
|
|
|
|
PROB pbTemp = gndrw.PbFault();
|
|
if(probTot>0.0)
|
|
pbTemp /= probTot;
|
|
gndrw.SetPbFault( pbTemp );
|
|
|
|
|
|
probLeak -= gndrw.PbFault();
|
|
}
|
|
|
|
ASSERT_THROW( probLeak >= - probTiny,
|
|
EC_INTERNAL_ERROR,
|
|
"fix/repair recommendations rescaling: residual probability too large" );
|
|
|
|
#ifdef _DEBUG
|
|
// Verify that there are no fixable nodes of signifcance beyond the new end point
|
|
int cBeyond = 0;
|
|
for ( ; ind < size(); ind++ )
|
|
{
|
|
GNODERECWORK & gndrw = self[ind];
|
|
|
|
if ( gndrw.PbFault() < probTiny )
|
|
continue; // highly unlikely to be significant
|
|
if ( ! gndrw.BFixable() )
|
|
continue;
|
|
}
|
|
assert( cBeyond == 0 );
|
|
#endif
|
|
|
|
// Resize to discard unfixable nodes
|
|
resize( i1stUnfix );
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
// VGNODERECWORK::Cost()
|
|
//
|
|
// purpose:
|
|
// calculate cost of a fix sequence (aka ECR(E)), given by
|
|
// Cost = Co1 + p1 * Cr1 + (1 - p1) * Co2 + p2 * Cr2 + ... + (1 - sum_i^N pi) Cservice
|
|
//
|
|
// The 'ielemFirst' argument, if non-zero, is the index of the element to treat as first.
|
|
// The 'piMinK' argument, if present, is set to the minimum K value computed.
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
COST VGNODERECWORK :: Cost (
|
|
int ielemFirst, // Element to consider as first in array
|
|
int * piMinK ) // Location to store minimum k
|
|
{
|
|
COST cost = 0.0;
|
|
PROB prob = 1.0;
|
|
const COST costService = MbnRec().CostService();
|
|
COST costK = costService * prob;
|
|
|
|
assert( _iFixedK == -1 || _iFixedK < size() );
|
|
int ielem = 0;
|
|
int iMinK = ielemFirst;
|
|
const COST costObsProbDef = MbnRec().CostObsProbDef();
|
|
int cSize = size();
|
|
|
|
#ifdef DUMP
|
|
cout << "\n\nVGNODERECWORK::Cost("
|
|
<< ielemFirst
|
|
<< "), _iFixedK = "
|
|
<< _iFixedK;
|
|
#endif
|
|
|
|
for ( int iel = 0; iel < cSize; iel++ )
|
|
{
|
|
// Select the array location, using ielemFirst (if present) as starting point,
|
|
// and skipping ielemFirst as necessary later.
|
|
ielem = iel == 0
|
|
? ielemFirst
|
|
: (iel - (ielemFirst > 0 && iel <= ielemFirst));
|
|
|
|
// Access the next element in the array
|
|
GNODERECWORK & gndrw = self[ielem];
|
|
GNODEREFP & gndref = gndrw.Gndref();
|
|
// If the node is unfixable, ignore it
|
|
if ( ! gndrw.BFixable() )
|
|
continue;
|
|
|
|
const PROB probFault = gndrw.PbFault();
|
|
COST costDelta = prob * gndref.CostObserve()
|
|
+ probFault * (gndref.CostFix() + costObsProbDef);
|
|
#ifdef DUMP
|
|
cout << "\n\t"
|
|
<< gndrw;
|
|
|
|
cout << "\n\t(iel="
|
|
<< iel
|
|
<< ",ielem="
|
|
<< ielem
|
|
<< ",size="
|
|
<< cSize
|
|
<< ")\n\t\tcostDelta("
|
|
<< costDelta
|
|
<< ") = prob("
|
|
<< prob
|
|
<< ") * costObs("
|
|
<< gndref.CostObserve()
|
|
<< ") + probFault("
|
|
<< probFault
|
|
<< ") * costFix("
|
|
<< gndref.CostFix()
|
|
<< ")"
|
|
;
|
|
#endif
|
|
|
|
cost += costDelta;
|
|
prob -= probFault;
|
|
// Compute the cost of the sequence if service is inserted here
|
|
COST costNow = cost + prob * costService;
|
|
|
|
#ifdef DUMP
|
|
cout << "\n\t\tcostPrior("
|
|
<< costK
|
|
<< "), costNow("
|
|
<< costNow
|
|
<< ") = cost("
|
|
<< cost
|
|
<< ") + prob("
|
|
<< prob
|
|
<< ") * costService("
|
|
<< costService
|
|
<< "), (prob ="
|
|
<< prob
|
|
<< ")";
|
|
|
|
cout.flush();
|
|
#endif
|
|
|
|
// Were we better off at the last step? Or is K fixed at this point?
|
|
if ( costNow < costK || iel == _iFixedK )
|
|
{
|
|
costK = costNow;
|
|
iMinK = ielem;
|
|
if ( iel == _iFixedK )
|
|
break; // We've reached the fixed point, so stop
|
|
}
|
|
|
|
ASSERT_THROW( prob >= - probTiny,
|
|
EC_INTERNAL_ERROR,
|
|
"fix/repair recommendations costing: probability underflow" );
|
|
}
|
|
|
|
#ifdef DUMP
|
|
cout << "\n\t** ielem="
|
|
<< ielem
|
|
<< ", first element = "
|
|
<< ielemFirst;
|
|
if ( _iFixedK < 0 )
|
|
cout << ", minimum k = " << iMinK;
|
|
else
|
|
cout << ", fixed k = " << _iFixedK;
|
|
cout << ", cost = "
|
|
<< costK
|
|
<< " (residual prob = "
|
|
<< prob
|
|
<< ")";
|
|
#endif
|
|
|
|
if ( _iFixedK < 0 )
|
|
{
|
|
if ( piMinK )
|
|
*piMinK = iMinK;
|
|
}
|
|
|
|
return costK;
|
|
}
|
|
|
|
// Set the cost of each node in the sequence
|
|
void VGNODERECWORK :: SetSequenceCost ()
|
|
{
|
|
// Reset any prior minimum fixed K
|
|
_iFixedK = -1;
|
|
// If "fixPlan", compute the minimum K only on the first cycle,
|
|
// then enforce it thereafter.
|
|
int iFixedK = -1;
|
|
|
|
for ( int ind = 0; ind < size(); ind++ )
|
|
{
|
|
// Compute the cost of the sequence with this node as first
|
|
COST cost = Cost( ind, & iFixedK );
|
|
|
|
// If not "fixplan", reset K for complete search next cycle
|
|
if ( MbnRec().ErcMethod() != MBNET_RECOMMENDER::ERCM_FixPlan )
|
|
iFixedK = -1;
|
|
else
|
|
// Else, if first cycle, fix K for remaining cycles.
|
|
if ( ind == 0 )
|
|
_iFixedK = iFixedK;
|
|
self[ind].SetCost( cost );
|
|
|
|
#ifdef DUMP
|
|
cout << "\nSetSequenceCost: "
|
|
<< self[ind]->Gndd().ZsrefName().Szc()
|
|
<< " = "
|
|
<< cost;
|
|
#endif
|
|
}
|
|
|
|
_iFixedK = -1;
|
|
_bSeqSet = true;
|
|
}
|
|
|
|
bool VGNODERECWORK :: BSameSequence ( const VGNODERECWORK & vgnw )
|
|
{
|
|
if ( size() != vgnw.size() )
|
|
return false;
|
|
for ( int ind = 0; ind < size(); ind++ )
|
|
{
|
|
if ( self[ind].Gndref() != vgnw[ind].Gndref() )
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
MBNET_RECOMMENDER :: MBNET_RECOMMENDER (
|
|
GOBJMBN_CLIQSET & inferEng,
|
|
ERCMETHOD ercm )
|
|
: MBNET_NODE_RANKER( inferEng.Model() ),
|
|
_inferEng( inferEng ),
|
|
_propMgr( inferEng.Model() ),
|
|
_ercm(ercm),
|
|
_err(EC_OK),
|
|
_pgnddPDAbnormal(NULL),
|
|
_costService(0.0),
|
|
_costObsProbDef(0.0),
|
|
_bReady(false)
|
|
{
|
|
}
|
|
|
|
MBNET_RECOMMENDER :: ~ MBNET_RECOMMENDER ()
|
|
{
|
|
}
|
|
|
|
|
|
//
|
|
// Return true if the network is in a proper state for recommendations
|
|
// Note that we don't check whether the network has been expanded or not.
|
|
// Since there must already be an inference engine, it's assumed that the
|
|
// network is in its correct state.
|
|
//
|
|
bool MBNET_RECOMMENDER :: BReady ()
|
|
{
|
|
MODEL::MODELENUM mdlenum( Model() );
|
|
_err = EC_OK;
|
|
|
|
_costService = CostServiceModel();
|
|
if ( _costService == 0.0 )
|
|
{
|
|
_err = EC_VOI_MODEL_COST_FIX;
|
|
return false;
|
|
}
|
|
|
|
// Clear the structure
|
|
_vpgnddFix.clear(); // Prepare to collect fixable nodes
|
|
_vpgndref.clear(); // Clear node reference array
|
|
|
|
// Iterate over the nodes in the network, checking constraints.
|
|
GELEMLNK * pgelm;
|
|
GNODEMBND * pgndd;
|
|
CLAMP clamp;
|
|
int cProbDefSet = 0; // # of instantiated PD nodes
|
|
int cFixSetAbnorm = 0; // # of fixables set to "abnormal"
|
|
int cInfo = 0; // # of info nodes
|
|
int cFixWithParents = 0; // # of fixables with parents
|
|
|
|
while ( pgelm = mdlenum.PlnkelNext() )
|
|
{
|
|
// Check only nodes
|
|
if ( pgelm->EType() != GOBJMBN::EBNO_NODE )
|
|
continue;
|
|
|
|
// We only support discrete nodes for now
|
|
DynCastThrow( pgelm, pgndd );
|
|
|
|
// See if it has a label
|
|
ESTDLBL eLbl = ELbl( *pgndd );
|
|
bool bRef = false;
|
|
switch ( eLbl )
|
|
{
|
|
case ESTDLBL_info:
|
|
cInfo++;
|
|
bRef = true;
|
|
break;
|
|
|
|
case ESTDLBL_problem:
|
|
InferGetEvidence( pgndd, clamp );
|
|
if ( clamp.BActive() && clamp.Ist() != istNormal )
|
|
{
|
|
cProbDefSet++; // Problem defining node set abnormal
|
|
_pgnddPDAbnormal = pgndd;
|
|
PROPMBN * ppropCostObs = _propMgr.PFind( *pgndd, ESTDP_cost_observe );
|
|
if ( ppropCostObs )
|
|
_costObsProbDef = ppropCostObs->Real();
|
|
}
|
|
break;
|
|
|
|
case ESTDLBL_fixobs:
|
|
case ESTDLBL_fixunobs:
|
|
case ESTDLBL_unfix:
|
|
// Collect fixable nodes
|
|
_vpgnddFix.push_back( pgndd );
|
|
|
|
// Check that it's not set abnormal
|
|
InferGetEvidence( pgndd, clamp );
|
|
if ( clamp.BActive() && clamp.Ist() != istNormal )
|
|
cFixSetAbnorm++; // Fixable node set abnormal
|
|
bRef = true;
|
|
if ( pgndd->CParent() > 0 )
|
|
cFixWithParents++; // Fixable node with parents
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// If necessary, create a reference item for this node
|
|
if ( bRef )
|
|
{
|
|
_vpgndref.push_back( new GNODEREFP( _propMgr, pgndd ) );
|
|
}
|
|
}
|
|
|
|
|
|
if ( cProbDefSet != 1 )
|
|
_err = EC_VOI_PROBDEF_ABNORMAL; // One and only one PD node must be abnormal
|
|
else
|
|
if ( cFixWithParents > 0 )
|
|
_err = EC_VOI_FIXABLE_PARENTS; // Some fixable node(s) has parents
|
|
else
|
|
if ( cFixSetAbnorm > 0 )
|
|
_err = EC_VOI_FIXABLE_ABNORMAL; // No fixable nodes can be abnormal
|
|
|
|
return _bReady = (_err == EC_OK);
|
|
}
|
|
|
|
// Interface to inference engine
|
|
void MBNET_RECOMMENDER :: InferGetBelief ( GNODEMBND * pgndd, MDVCPD & mdvBel )
|
|
{
|
|
InferEng().GetBelief( pgndd, mdvBel );
|
|
}
|
|
|
|
void MBNET_RECOMMENDER :: InferGetEvidence ( GNODEMBND * pgndd, CLAMP & clamp )
|
|
{
|
|
InferEng().GetEvidence( pgndd, clamp );
|
|
}
|
|
|
|
void MBNET_RECOMMENDER :: InferEnterEvidence ( GNODEMBND * pgndd, const CLAMP & clamp )
|
|
{
|
|
InferEng().EnterEvidence( pgndd, clamp );
|
|
}
|
|
|
|
bool MBNET_RECOMMENDER :: BInferImpossible ()
|
|
{
|
|
return InferEng().BImpossible();
|
|
}
|
|
|
|
void MBNET_RECOMMENDER :: PrintInstantiations ()
|
|
{
|
|
#ifdef DUMP
|
|
|
|
GELEMLNK * pgelm;
|
|
GNODEMBND * pgndd;
|
|
CLAMP clamp;
|
|
|
|
cout << "\n\tInstantiations:";
|
|
|
|
MODEL::MODELENUM mdlenum( Model() );
|
|
while ( pgelm = mdlenum.PlnkelNext() )
|
|
{
|
|
// Check only nodes
|
|
if ( pgelm->EType() != GOBJMBN::EBNO_NODE )
|
|
continue;
|
|
|
|
// We only support discrete nodes for now
|
|
DynCastThrow( pgelm, pgndd );
|
|
InferGetEvidence( pgndd, clamp );
|
|
if ( clamp.BActive() )
|
|
{
|
|
cout << "\n\t\tnode "
|
|
<< pgndd->ZsrefName().Szc()
|
|
<< " is instantiated to state "
|
|
<< clamp.Ist()
|
|
<< ", "
|
|
<< pgndd->VzsrStates()[clamp.Ist()].Szc();
|
|
}
|
|
}
|
|
cout << "\n\tInstantiations end.";
|
|
#endif
|
|
}
|
|
|
|
COST MBNET_RECOMMENDER :: CostServiceModel ()
|
|
{
|
|
// Get the model's cost-to-fix as service cost.
|
|
PROPMBN * ppropFixCost = _propMgr.PFind( ESTDP_cost_fix );
|
|
COST costService = ppropFixCost
|
|
? ppropFixCost->Real()
|
|
: 0.0;
|
|
|
|
return costService;
|
|
}
|
|
|
|
// Look up the label property of a node; convert to standard enumeration value.
|
|
ESTDLBL MBNET_RECOMMENDER :: ELbl ( GNODEMBN & gnd )
|
|
{
|
|
PROPMBN * propLbl = PropMgr().PFind( gnd, ESTDP_label );
|
|
if ( ! propLbl )
|
|
return ESTDLBL_other;
|
|
|
|
int iUserLbl = propLbl->Real();
|
|
int iLbl = PropMgr().IUserToLbl( propLbl->Real() );
|
|
return iLbl < 0
|
|
? ESTDLBL_other
|
|
: (ESTDLBL) iLbl;
|
|
}
|
|
|
|
// Enter evidence for a troubleshooting model.
|
|
//
|
|
// If the node is a fixable node being "set" to "normal", uninstantiate all
|
|
// information nodes downstream from it.
|
|
//
|
|
void MBNET_RECOMMENDER :: EnterEvidence (
|
|
GNODEMBND * pgndd,
|
|
const CLAMP & clamp,
|
|
bool bSet )
|
|
{
|
|
if ( bSet )
|
|
{
|
|
ESTDLBL eLbl = ELbl( *pgndd );
|
|
switch ( eLbl )
|
|
{
|
|
case ESTDLBL_unfix:
|
|
case ESTDLBL_fixobs:
|
|
case ESTDLBL_fixunobs:
|
|
{
|
|
// This is a fixable node
|
|
if ( ! clamp.BActive() )
|
|
break; // Node is being unset
|
|
if ( clamp.Ist() != istNormal )
|
|
break; // Node is not being fixed
|
|
|
|
// Find all downstream information nodes which are instantiated.
|
|
VPGNODEMBND vpgndd;
|
|
vpgndd.push_back(pgndd);
|
|
ExpandDownstream(vpgndd);
|
|
CLAMP clampInfo;
|
|
for ( int ind = 0; ind < vpgndd.size(); ind++ )
|
|
{
|
|
GNODEMBND * pgnddInfo = vpgndd[ind];
|
|
ESTDLBL l = ELbl( *pgnddInfo );
|
|
if ( l != ESTDLBL_info )
|
|
continue;
|
|
InferGetEvidence( pgnddInfo, clampInfo );
|
|
if ( ! clampInfo.BActive() )
|
|
continue;
|
|
// This is a clamped information node downstream from the fixable
|
|
// node being repaired. Unset its instantiation.
|
|
InferEnterEvidence( pgnddInfo, CLAMP() );
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
InferEnterEvidence( pgndd, clamp );
|
|
}
|
|
|
|
//
|
|
// Compute the probability distribution of the node and compare it to
|
|
// the stored distribution. Return true If it has changed.
|
|
//
|
|
bool MBNET_RECOMMENDER :: BProbsChange ( GPNDDDIST & gpndddist )
|
|
{
|
|
MDVCPD mdv;
|
|
// The the distribution given the current state of evidence
|
|
InferGetBelief( gpndddist.Pgnd(), mdv );
|
|
// Compare it to the other distribution
|
|
MDVCPD & mdvo = gpndddist.Dist();
|
|
int cprob = mdvo.first.size();
|
|
assert( mdv.first.size() == cprob );
|
|
|
|
for ( int i = 0; i < cprob; i++ )
|
|
{
|
|
#ifdef DUMP
|
|
cout << "\n\t\tBProbsChange, state = "
|
|
<< i
|
|
<< ", old = "
|
|
<< mdvo[i]
|
|
<< ", new = "
|
|
<< mdv[i];
|
|
#endif
|
|
if ( ! BEqual( mdv[i], mdvo[i] ) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Add to the given array all nodes which are downstream from members
|
|
void MBNET_RECOMMENDER :: ExpandDownstream ( VPGNODEMBND & vpgndd )
|
|
{
|
|
Model().ClearNodeMarks();
|
|
// Mark all nodes downstream of every given node
|
|
for ( int i = 0; i < vpgndd.size(); i++ )
|
|
{
|
|
vpgndd[i]->Visit(false);
|
|
}
|
|
|
|
// Collect those nodes
|
|
MODEL::MODELENUM mdlenum( Model() );
|
|
GELEMLNK * pgelm;
|
|
GNODEMBND * pgndd;
|
|
while ( pgelm = mdlenum.PlnkelNext() )
|
|
{
|
|
if ( pgelm->EType() != GOBJMBN::EBNO_NODE )
|
|
continue;
|
|
|
|
// We only support discrete nodes for now
|
|
DynCastThrow( pgelm, pgndd );
|
|
// Add marked nodes which are not already present
|
|
if ( pgndd->IMark() )
|
|
{
|
|
appendset( vpgndd, pgndd );
|
|
}
|
|
}
|
|
}
|
|
|
|
void MBNET_RECOMMENDER :: DetermineRelevantFixableNodes (
|
|
VGPNDDDIST & vgndddFixRelevant,
|
|
bool bUsePriorList,
|
|
GNODEMBND * pgnddInfoPlan /* = NULL */ )
|
|
{
|
|
assert( _vpgnddFix.size() > 0 );
|
|
assert( _pgnddPDAbnormal != NULL );
|
|
|
|
#ifdef DUMP
|
|
cout << "\nRecommendations, DetermineRelevantFixableNodes: abnormal PD node is "
|
|
<< _pgnddPDAbnormal->ZsrefName().Szc();
|
|
if ( bUsePriorList )
|
|
cout << " (secondary invocation)";
|
|
#endif
|
|
|
|
/*
|
|
If 'bUsePriorList' is false:
|
|
Find all the relevant fixable nodes; i.e., those fixable nodes which
|
|
linked to the Problem node and which are not clamped. If unfixed,
|
|
(that is, not repaired and not "unfixable"), accumulate them for a
|
|
search of relevant info nodes.
|
|
First, visit the problem defining node which is instantiated to an
|
|
abnormal state and mark all upstream links to it.
|
|
Else, if 'bUsePriorList' is true:
|
|
Use the relevant fixable list previously accumulated
|
|
*/
|
|
|
|
vgndddFixRelevant.clear(); // clear the result array
|
|
int cfix = 0; // count of fixables to search
|
|
if ( bUsePriorList )
|
|
{
|
|
// Use the original list of relevant fixables
|
|
cfix = _vgndddFixRelevant.size();
|
|
}
|
|
else
|
|
{
|
|
// Fill in a new list of releveant fixables
|
|
Model().ClearNodeMarks();
|
|
_pgnddPDAbnormal->Visit();
|
|
cfix = _vpgnddFix.size();
|
|
}
|
|
|
|
// Accumulate the list of relevant, available (unfixed) fixable nodes, to
|
|
// which downstream info nodes will be added
|
|
VPGNODEMBND vpgnddDownstreamFromRelevantFixable;
|
|
int irel = 0;
|
|
for ( int ifix = 0; ifix < cfix; ifix++ )
|
|
{
|
|
GNODEMBND * pgnddFix;
|
|
if ( bUsePriorList )
|
|
{ // Use prior list element
|
|
pgnddFix = _vgndddFixRelevant[ifix].Pgnd();
|
|
}
|
|
else
|
|
{ // See if this node was marked by "visit" above
|
|
pgnddFix = _vpgnddFix[ifix];
|
|
if ( pgnddFix->IMark() == 0 )
|
|
continue; // unconnected to current problem
|
|
|
|
CLAMP clampFix;
|
|
InferGetEvidence( pgnddFix, clampFix );
|
|
if ( clampFix.BActive() )
|
|
continue; // Fixable node has been fixed; irrelevant
|
|
}
|
|
|
|
// This is an unfixed, fixable node involved in the problem;
|
|
// append it to the list
|
|
vgndddFixRelevant.resize(irel+1);
|
|
GPNDDDIST & gpnddd = vgndddFixRelevant[irel++];
|
|
gpnddd.Pgnd() = pgnddFix;
|
|
// Get its current PD and save it
|
|
InferGetBelief( gpnddd.Pgnd(), gpnddd.Dist() );
|
|
// If fixable, add it to the list for accumulation of relevant info nodes
|
|
ESTDLBL eLbl = ELbl( *pgnddFix );
|
|
if ( eLbl == ESTDLBL_fixobs || eLbl == ESTDLBL_fixunobs )
|
|
{
|
|
vpgnddDownstreamFromRelevantFixable.push_back( pgnddFix );
|
|
}
|
|
}
|
|
|
|
#ifdef DUMP
|
|
cout << "\n\tInstantiations before relevance check";
|
|
PrintInstantiations();
|
|
#endif
|
|
|
|
// Uninstantiate the info nodes which are downstream from any
|
|
// RELEVANT UNFIXED fixable nodes. The first step, which is to gather such
|
|
// relevant fixable nodes, has been done above.
|
|
//
|
|
// Note that this is NOT done for the info node being used for INFOPLAN (ECO)
|
|
// generation. Since INFOPLAN::INFOPLAN precesses this node through its states,
|
|
// it's pointless to uninstantiate it here.
|
|
//
|
|
// Next, find all info nodes downstream from the relevant unfixed fixables.
|
|
// Finally, temporarily rescind the instantiations of those info nodes.
|
|
|
|
VPNDD_IST vpnddIstReset; // remember pairs of node pointers and ISTs to reset later
|
|
|
|
// Number of unfixed fixables so far
|
|
int cUnfixedNodes = vpgnddDownstreamFromRelevantFixable.size();
|
|
// Expand the collection to include all downstream nodes
|
|
ExpandDownstream( vpgnddDownstreamFromRelevantFixable );
|
|
// Get number of relevant info nodes
|
|
int cInfoNodes = vpgnddDownstreamFromRelevantFixable.size() - cUnfixedNodes;
|
|
CLAMP clampInfo;
|
|
CLAMP clampReset;
|
|
int ireset = 0;
|
|
|
|
#ifdef DUMP
|
|
cout << "\n\t"
|
|
<< cUnfixedNodes
|
|
<< " fixable nodes are upstream of PD, "
|
|
<< cInfoNodes
|
|
<< " nodes are downstream from them";
|
|
#endif
|
|
|
|
for ( int iinfo = cUnfixedNodes;
|
|
iinfo < vpgnddDownstreamFromRelevantFixable.size();
|
|
iinfo++ )
|
|
{
|
|
GNODEMBND * pgnddInfo = vpgnddDownstreamFromRelevantFixable[iinfo];
|
|
if ( ELbl( *pgnddInfo ) != ESTDLBL_info )
|
|
continue; // Not an info node
|
|
if ( pgnddInfo == pgnddInfoPlan )
|
|
continue; // The info node we're planning for
|
|
InferGetEvidence( pgnddInfo, clampInfo );
|
|
if ( ! clampInfo.BActive() )
|
|
continue; // Not clamped
|
|
#ifdef DUMP
|
|
cout << "\n\tinfo node "
|
|
<< pgnddInfo->ZsrefName().Szc()
|
|
<< " is being unclamped from state "
|
|
<< clampInfo.Ist();
|
|
#endif
|
|
// Instantiated info node. Save its ptr and current state for later.
|
|
vpnddIstReset.push_back( PNDD_IST( pgnddInfo, clampInfo.Ist() ) );
|
|
// Unclamp it for relevance check
|
|
InferEnterEvidence( pgnddInfo, clampReset );
|
|
}
|
|
|
|
// Walk the list of relevant fixables accumulated so far and determine those
|
|
// which are probabilistically relevant. Move those which are to the front
|
|
// of the relevance array, then chop the stragglers off the end.
|
|
|
|
// Get the current state of the PD node
|
|
CLAMP clampProblem;
|
|
InferGetEvidence( _pgnddPDAbnormal, clampProblem );
|
|
IST istProblemSet = clampProblem.Ist();
|
|
|
|
#ifdef DUMP
|
|
cout << "\n\tInstantiations during relevance check";
|
|
PrintInstantiations();
|
|
#endif
|
|
|
|
// Iterate over all open (non-evidenced) states of the problem defining node.
|
|
int cNodeFix = vgndddFixRelevant.size();
|
|
int cRelevant = 0;
|
|
for ( IST istProblem = 0; istProblem < _pgnddPDAbnormal->CState(); istProblem++ )
|
|
{
|
|
// If we've already stored every possible relevant fixable node, quit
|
|
if ( cRelevant == cUnfixedNodes )
|
|
break;
|
|
// If this is the current problem state, skip it
|
|
if ( istProblem == istProblemSet )
|
|
continue;
|
|
|
|
// Temporarily instantiate the PD node to this alternative state
|
|
InferEnterEvidence( _pgnddPDAbnormal, CLAMP(true, istProblem, true) );
|
|
// If state of evidence is impossible, continue
|
|
if ( BInferImpossible() )
|
|
continue;
|
|
|
|
// Iterate over the remaining relevant fixable nodes. As they are found to be
|
|
// relevant, the nodes are moved to the front of the array and not checked again.
|
|
for ( int inode = cRelevant; inode < cNodeFix; inode++ )
|
|
{
|
|
GPNDDDIST & gpndddist = vgndddFixRelevant[inode];
|
|
GNODEMBND * pgnddFix = gpndddist.Pgnd();
|
|
CLAMP clampFix;
|
|
InferGetEvidence( pgnddFix, clampFix );
|
|
if ( clampFix.BActive() && clampFix.Ist() == istNormal )
|
|
continue; // This fixable node has been fixed and is irrelevant
|
|
|
|
// If the PD of this fixable node changes for this problem instantiation,
|
|
// it's relevant; move it to front of array.
|
|
if ( BProbsChange( gpndddist ) )
|
|
{
|
|
#ifdef DUMP
|
|
cout << "\n\tfixable node "
|
|
<< pgnddFix->ZsrefName().Szc()
|
|
<< " is probabilistically relevant ";
|
|
#endif
|
|
vswap( vgndddFixRelevant, cRelevant++, inode );
|
|
}
|
|
#ifdef DUMP
|
|
else
|
|
{
|
|
cout << "\n\tfixable node "
|
|
<< pgnddFix->ZsrefName().Szc()
|
|
<< " is NOT probabilistically relevant ";
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// Resize the computed array to chop off the irrelevant nodes
|
|
vgndddFixRelevant.resize( cRelevant );
|
|
|
|
// Reset the probdef node back to its current instantiation
|
|
InferEnterEvidence( _pgnddPDAbnormal, clampProblem );
|
|
|
|
// Reset the uninstantiated info nodes back to their prior states
|
|
for ( ireset = 0; ireset < vpnddIstReset.size(); ireset++ )
|
|
{
|
|
IST ist = vpnddIstReset[ireset].second;
|
|
GNODEMBND * pgndd = vpnddIstReset[ireset].first;
|
|
CLAMP clampReset(true, ist, true);
|
|
InferEnterEvidence( pgndd, clampReset );
|
|
}
|
|
|
|
#ifdef DUMP
|
|
if ( cRelevant )
|
|
{
|
|
cout << "\nRecommendations, DetermineRelevantFixableNodes: relevant fixables are: " ;
|
|
for ( int ifx = 0; ifx < vgndddFixRelevant.size(); ifx++ )
|
|
{
|
|
cout << "\n\tnode "
|
|
<< vgndddFixRelevant[ifx].Pgnd()->ZsrefName().Szc()
|
|
<< " is relevant fixable #"
|
|
<< ifx;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cout << "\nRecommendations, DetermineRelevantFixableNodes: there are NO relevant fixables " ;
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
void MBNET_RECOMMENDER :: ComputeFixSequence (
|
|
VGPNDDDIST & vgndddFixRelevant, // IN: Relevant fixable nodes
|
|
VGNODERECWORK & vgnrwFix ) // OUT: Ordered fix/repair sequence
|
|
{
|
|
// Using the array of node references and the array of relevant fixable nodes,
|
|
// initialize the fix/repair sequence array.
|
|
vgnrwFix.resize( vgndddFixRelevant.size() ) ;
|
|
for ( int ind = 0; ind < vgnrwFix.size(); ind++ )
|
|
{
|
|
GNODEMBND * pgndd = vgndddFixRelevant[ind].Pgnd();
|
|
vgnrwFix.InitElem( pgndd, ind );
|
|
}
|
|
|
|
VGNODERECWORK::ESORT esort = VGNODERECWORK::ESRT_ProbOverCost;
|
|
switch ( _ercm )
|
|
{
|
|
case ERCM_MostLikely:
|
|
esort = VGNODERECWORK::ESRT_SgnProb;
|
|
break;
|
|
case ERCM_Cheap:
|
|
esort = VGNODERECWORK::ESRT_NegCost;
|
|
break;
|
|
}
|
|
vgnrwFix.Sort( esort );
|
|
vgnrwFix.Rescale();
|
|
|
|
#ifdef DUMP
|
|
cout << "\nRecommendations, ComputeFixSequence: fix/repair sequence is:";
|
|
for ( ind = 0; ind < vgnrwFix.size(); ind++ )
|
|
{
|
|
GNODEREFP & gndref = vgnrwFix[ind].Gndref();
|
|
cout << "\n\tnode "
|
|
<< ind
|
|
<< " is "
|
|
<< gndref.Gndd().ZsrefName().Szc()
|
|
<< ", p/c = "
|
|
<< vgnrwFix[ind].PbOverCost()
|
|
<< ", utility = "
|
|
<< gndref.Util();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
// Identify the relevant info nodes and compute their costs.
|
|
// Formerly "BxComputeCosts()"
|
|
void MBNET_RECOMMENDER :: DetermineRelevantInfoNodes (
|
|
VGNODERECWORK & vgnrwFix,
|
|
VGNODERECWORK & vgnrwInfo )
|
|
{
|
|
assert( _pgnddPDAbnormal != NULL );
|
|
CLAMP clampInfo;
|
|
|
|
vgnrwInfo.clear();
|
|
|
|
#ifdef DUMP
|
|
cout << "\nRecommendations, DetermineRelevantInfoNodes:";
|
|
#endif
|
|
|
|
for ( int ind = 0; ind < _vpgndref.size(); ind++ )
|
|
{
|
|
GNODEREFP * pgndref = _vpgndref[ind];
|
|
assert( pgndref );
|
|
if ( pgndref->ELbl() != ESTDLBL_info )
|
|
continue;
|
|
InferGetEvidence( pgndref->Pgndd(), clampInfo );
|
|
// Instantiated info nodes are irrelevant
|
|
if ( clampInfo.BActive() )
|
|
continue;
|
|
|
|
// Create an array of fix/repair plans for all states of this info node
|
|
INFOPLAN infoplan( self, pgndref->Gndd(), vgnrwFix );
|
|
|
|
// If all plans result in the same sequence, it's irrelevant
|
|
if ( infoplan.BSameSequence() )
|
|
{
|
|
#ifdef DUMP
|
|
cout << "\n\tinfo node "
|
|
<< pgndref->Gndd().ZsrefName().Szc()
|
|
<< " is NOT relevant; all plans are the same";
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
// Add this info node to the array
|
|
vgnrwInfo.InitElem( pgndref->Pgndd() );
|
|
|
|
// Set the utility to be the negative of the plan cost
|
|
COST cost = infoplan.Cost();
|
|
pgndref->Util() = - cost;
|
|
|
|
#ifdef DUMP
|
|
cout << "\n\tinfo node "
|
|
<< pgndref->Gndd().ZsrefName().Szc()
|
|
<< " is relevant, utility = "
|
|
<< pgndref->Util();
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void MBNET_RECOMMENDER :: operator () ()
|
|
{
|
|
// If BReady() has not been called yet, do it now.
|
|
if ( ! _bReady )
|
|
{
|
|
if ( ! BReady() )
|
|
throw GMException( _err, "network state invalid for recommendations" );
|
|
}
|
|
|
|
#ifdef DUMP
|
|
cout.precision(8);
|
|
#endif
|
|
|
|
// Clear the "ready" flag; i.e., force subsequent call to BReady().
|
|
Unready();
|
|
|
|
if ( _ercm != ERCM_FixPlan )
|
|
throw GMException( EC_NYI, "only fix/plan recommendations supported" );
|
|
|
|
assert( _pgnddPDAbnormal );
|
|
|
|
// Array of fixable nodes
|
|
VGNODERECWORK vgnrwFix( this );
|
|
// Array of informational nodes
|
|
VGNODERECWORK vgnrwInfo( this );
|
|
|
|
// Collect the relevant fixable nodes
|
|
DetermineRelevantFixableNodes( _vgndddFixRelevant, false, NULL );
|
|
|
|
// Collect and order the relevant fixable node information,
|
|
// sorted according to planning method and rescaled.
|
|
ComputeFixSequence( _vgndddFixRelevant, vgnrwFix );
|
|
|
|
// Compute ECR, the expected cost of repair.
|
|
vgnrwFix.SetSequenceCost();
|
|
|
|
// If information nodes are relevant, determine the set of them.
|
|
if ( _ercm == ERCM_FixPlan || _ercm == ERCM_FixPlanOnly )
|
|
{
|
|
// Compute ECO, the expected cost of the Observation-Repair sequence.
|
|
DetermineRelevantInfoNodes( vgnrwFix, vgnrwInfo );
|
|
}
|
|
|
|
// Collect all relevant fixables and infos and sort them
|
|
VGNODERECWORK vgnrwRecom( this );
|
|
vgnrwRecom.resize( vgnrwFix.size() + vgnrwInfo.size() );
|
|
|
|
// Add fixables...
|
|
for ( int ind = 0; ind < vgnrwFix.size(); ind++ )
|
|
{
|
|
vgnrwRecom[ind] = vgnrwFix[ind];
|
|
}
|
|
// Add infos...
|
|
int indStart = ind;
|
|
for ( ind = 0; ind < vgnrwInfo.size(); ind++ )
|
|
{
|
|
vgnrwRecom[indStart + ind] = vgnrwInfo[ind];
|
|
}
|
|
|
|
// Sort by negative utility
|
|
vgnrwRecom.Sort( VGNODERECWORK::ESRT_SgnUtil );
|
|
|
|
// Copy information to the output areas, ordered by lowest cost.
|
|
// First, determine how many are more expensive than a service call
|
|
// since we discard those.
|
|
int cRecom = vgnrwRecom.size();
|
|
int iRecom = 0;
|
|
if ( _costService != 0.0 )
|
|
{
|
|
for ( iRecom = 0; iRecom < cRecom; iRecom++ )
|
|
{
|
|
COST cost = vgnrwRecom[iRecom].Gndref().Util();
|
|
if ( cost >= _costService )
|
|
break;
|
|
}
|
|
cRecom = iRecom;
|
|
}
|
|
|
|
_vzsrNodes.resize(cRecom);
|
|
_vlrValues.resize(cRecom);
|
|
|
|
for ( iRecom = 0; iRecom < cRecom; iRecom++ )
|
|
{
|
|
GNODEREFP & gndref = vgnrwRecom[iRecom].Gndref();
|
|
// Add the node name to the list
|
|
_vzsrNodes[iRecom] = gndref.Gndd().ZsrefName();
|
|
// and give its score (utility)
|
|
_vlrValues[iRecom] = gndref.Util();
|
|
|
|
#ifdef DUMP
|
|
cout << "\nRecommendation # "
|
|
<< iRecom
|
|
<< ", node "
|
|
<< _vzsrNodes[iRecom].Szc()
|
|
<< " = "
|
|
<< _vlrValues[iRecom];
|
|
cout.flush();
|
|
#endif
|
|
}
|
|
}
|
|
|