Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1425 lines
36 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1997 - 1998
  6. //
  7. // File: recomend.cpp
  8. //
  9. //--------------------------------------------------------------------------
  10. //
  11. // recomend.cpp: Fix-or-repair planning recommendations
  12. //
  13. #include <basetsd.h>
  14. #include <math.h>
  15. #include <float.h>
  16. #include "algos.h"
  17. #include "recomend.h"
  18. #include "parmio.h"
  19. #ifdef _DEBUG
  20. //#define DUMP // Uncomment for copious diagnostic output
  21. #endif
  22. const PROB probTiny = 1e-6; // General probability tolerance
  23. static
  24. ostream & operator << ( ostream & os, GPNDDDIST & gpnddist )
  25. {
  26. os << "GPNDDDIST: ";
  27. if ( gpnddist.Pgnd() )
  28. {
  29. os << gpnddist.Pgnd()->ZsrefName().Szc()
  30. << ", distribution: "
  31. << gpnddist.Dist();
  32. }
  33. else
  34. {
  35. os << "<NULL>";
  36. }
  37. return os;
  38. }
  39. static
  40. ostream & operator << ( ostream & os, GNODEREFP & gndref )
  41. {
  42. assert( gndref.Pgndd() );
  43. os << "GNODEREFP: "
  44. << gndref.Gndd().ZsrefName().Szc()
  45. << " (obs = "
  46. << gndref.CostObserve()
  47. << ", fix = "
  48. << gndref.CostFix()
  49. << ", util = "
  50. << gndref.Util()
  51. << ", lbl = "
  52. << PROPMGR::SzcLbl( gndref.ELbl() )
  53. << ")";
  54. return os;
  55. }
  56. static
  57. ostream & operator << ( ostream & os, GNODERECWORK & gnrw )
  58. {
  59. os << "GNODERECWORK: "
  60. << gnrw.Gndref()
  61. << ", p/c = "
  62. << gnrw.PbOverCost()
  63. << ", p(fault) = "
  64. << gnrw.PbFault();
  65. return os;
  66. }
  67. static
  68. inline
  69. bool BIsUnity( const REAL & r )
  70. {
  71. return 1.0 - probTiny < r && r < 1.0 + probTiny;
  72. }
  73. static
  74. inline
  75. bool BEqual ( const REAL & ra, const REAL & rb )
  76. {
  77. //return fabs( ra - rb ) <= probTiny;
  78. return ra != 0.0
  79. ? BIsUnity( rb / ra )
  80. : rb == 0.0;
  81. }
  82. //
  83. // Ordering routines for arrays of GNODERECWORKs
  84. //
  85. typedef binary_function<const GNODERECWORK &, const GNODERECWORK &, bool> SORTGNODERECWORK;
  86. // The greater the prob-over-cost, the lower the sort order
  87. class SRTGNW_SgnProbOverCost : public SORTGNODERECWORK
  88. {
  89. public:
  90. bool operator () (const GNODERECWORK & gnwa, const GNODERECWORK & gnwb) const
  91. {
  92. PROB pra = gnwa.PbOverCost();
  93. PROB prb = gnwb.PbOverCost();
  94. return pra > prb;
  95. }
  96. };
  97. // The greater the prob fault, the lower the sort order
  98. class SRTGNW_SgnProb : public SORTGNODERECWORK
  99. {
  100. public:
  101. bool operator () (const GNODERECWORK & gnwa, const GNODERECWORK & gnwb) const
  102. {
  103. // Force leak terms to sort high
  104. int iLeak = 0;
  105. if ( ! gnwa->BLeak() && gnwb->BLeak() )
  106. iLeak = -1; // Unleak < leak
  107. else
  108. if ( gnwa->BLeak() && ! gnwb->BLeak() )
  109. iLeak = 1; // Leak > Unleak
  110. if ( iLeak != 0 )
  111. return iLeak;
  112. PROB pra = gnwa.PbFault();
  113. PROB prb = gnwb.PbFault();
  114. return pra > prb;
  115. }
  116. };
  117. // The lower the cost-to-observe, the lower the sort order
  118. class SRTGNW_SgnNegCost : public SORTGNODERECWORK
  119. {
  120. public:
  121. bool operator () (const GNODERECWORK & gnwa, const GNODERECWORK & gnwb) const
  122. {
  123. COST costa = gnwa.CostObsIfFixable();
  124. COST costb = gnwb.CostObsIfFixable();
  125. return costa < costb;
  126. }
  127. };
  128. // The higher the utility, the lower the sort order
  129. class SRTGNW_SgnUtil : public SORTGNODERECWORK
  130. {
  131. public:
  132. bool operator () (const GNODERECWORK & gnwa, const GNODERECWORK & gnwb) const
  133. {
  134. COST utila = gnwa.Gndref().Util();
  135. COST utilb = gnwb.Gndref().Util();
  136. return utila > utilb;
  137. }
  138. };
  139. //
  140. // Construct a node reference object. Extract properties, etc.
  141. //
  142. GNODEREFP :: GNODEREFP ( PROPMGR & propMgr, GNODEMBND * pgndd )
  143. :_pgndd(pgndd),
  144. _costObserve(0.0),
  145. _costFix(0.0),
  146. _costUtil(0.0),
  147. _eLbl(ESTDLBL_other)
  148. {
  149. ASSERT_THROW( pgndd, EC_NULLP, "invalid GNOEREFP construction" );
  150. PROPMBN * pprop = propMgr.PFind( *pgndd, ESTDP_cost_fix );
  151. if ( pprop )
  152. _costFix = pprop->Real();
  153. pprop = propMgr.PFind( *pgndd, ESTDP_cost_observe );
  154. if ( pprop )
  155. _costObserve = pprop->Real();
  156. pprop = propMgr.PFind( *pgndd, ESTDP_label );
  157. if ( pprop )
  158. _eLbl = (ESTDLBL) propMgr.IUserToLbl( pprop->Real() );
  159. _bLeak = pgndd->BFlag( EIBF_Leak );
  160. // If it's unobservable, use cost-to-fix as cost-to-observe
  161. if ( _eLbl == ESTDLBL_fixunobs && _costObserve == 0.0 )
  162. {
  163. _costObserve = _costFix;
  164. _costFix = 0.0;
  165. }
  166. }
  167. // Initialize a work record from a node reference object and its fault probability
  168. void GNODERECWORK :: Init ( GNODEREFP * pgndref, PROB pbFault )
  169. {
  170. _pgndref = pgndref;
  171. _pbFault = pbFault;
  172. _pbOverCost = 0.0;
  173. if ( BFixable() )
  174. {
  175. COST costObserve = _pgndref->CostObserve();
  176. if ( costObserve != 0.0 )
  177. _pbOverCost = _pbFault / costObserve;
  178. assert( _finite( _pbOverCost ) );
  179. }
  180. }
  181. // Initialize a work record from a node reference object
  182. void GNODERECWORK :: Init ( MBNET_RECOMMENDER & mbnRecom, GNODEREFP * pgndref )
  183. {
  184. MDVCPD mdv;
  185. _pgndref = pgndref;
  186. mbnRecom.InferGetBelief( _pgndref->Pgndd(), mdv );
  187. Init( pgndref, 1.0 - mdv[0] );
  188. }
  189. void VGNODERECWORK :: InitElem ( GNODEREFP * pgndref, int index /* = -1 */ )
  190. {
  191. // Grow the array as needed
  192. if ( index < 0 )
  193. index = size();
  194. if ( index >= size() )
  195. resize( index+1 );
  196. // Initialize the element
  197. self[index].Init( MbnRec(), pgndref );
  198. }
  199. void VGNODERECWORK :: InitElem ( GNODEMBND * pgndd, int index )
  200. {
  201. // Find the node reference record in the recommendations object's array
  202. VPGNODEREFP & vpgndref = MbnRec().Vpgndref();
  203. int indref = vpgndref.ifind( pgndd );
  204. ASSERT_THROW( indref >= 0,
  205. EC_INTERNAL_ERROR,
  206. "node ref not found during recommendations" );
  207. // Initialize using that reference
  208. InitElem( vpgndref[indref], index );
  209. }
  210. COST VGNODERECWORK :: CostService () const
  211. {
  212. return MbnRec().CostService();
  213. }
  214. void VGNODERECWORK :: Sort ( ESORT esort )
  215. {
  216. iterator ibeg = begin();
  217. iterator iend = end();
  218. switch ( esort )
  219. {
  220. case ESRT_ProbOverCost:
  221. {
  222. sort( ibeg, iend, SRTGNW_SgnProbOverCost() );
  223. break;
  224. }
  225. case ESRT_SgnProb:
  226. {
  227. sort( ibeg, iend, SRTGNW_SgnProb() );
  228. break;
  229. }
  230. case ESRT_NegCost:
  231. {
  232. sort( ibeg, iend, SRTGNW_SgnNegCost() );
  233. break;
  234. }
  235. case ESRT_SgnUtil:
  236. {
  237. sort( ibeg, iend, SRTGNW_SgnUtil() );
  238. break;
  239. }
  240. default:
  241. {
  242. THROW_ASSERT( EC_INTERNAL_ERROR, "invalid sort selector in recommendations" );
  243. break;
  244. }
  245. }
  246. }
  247. /////////////////////////////////////////////////////////////////////////////////////////
  248. //
  249. // class INFOPLAN:
  250. // Encloses an array of VGNODERECWORKs, each of which is a fix-and-repair
  251. // sequence corresponding to a particular state of an informational node.
  252. //
  253. /////////////////////////////////////////////////////////////////////////////////////////
  254. class INFOPLAN
  255. {
  256. public:
  257. INFOPLAN ( MBNET_RECOMMENDER & mbnRec, // The recommendations object
  258. GNODEMBND & gndInfo, // The information node
  259. VGNODERECWORK & vgndrwFixRepair ); // The existing f-r sequence
  260. // Compute the cost of the sequence
  261. COST Cost();
  262. // Return true if all plans are equivalent
  263. bool BSameSequence() { return _bSameSequence; };
  264. protected:
  265. MBNET_RECOMMENDER & _mbnRec; // The recommendations object
  266. GNODEMBND & _gndInfo; // The info node represented
  267. MDVCPD _dd; // Unconditional probability distribution
  268. VVGNODERECWORK _vvgndrw; // Array of plan arrays
  269. bool _bSameSequence; // True if all plans are equivalent
  270. };
  271. INFOPLAN :: INFOPLAN (
  272. MBNET_RECOMMENDER & mbnRec,
  273. GNODEMBND & gndInfo,
  274. VGNODERECWORK & vgndrwFixRepair )
  275. : _mbnRec(mbnRec),
  276. _gndInfo(gndInfo),
  277. _bSameSequence(false)
  278. {
  279. #ifdef DUMP
  280. cout << "\nINFOPLAN::INFOPLAN: info node "
  281. << gndInfo.ZsrefName().Szc();
  282. #endif
  283. CLAMP clampInfo; // State of info node at call time
  284. _mbnRec.InferGetEvidence( & _gndInfo, clampInfo );
  285. assert( ! clampInfo.BActive() );
  286. assert( _mbnRec.ELbl( _gndInfo ) == ESTDLBL_info );
  287. // Get setup information
  288. GNODEMBND * pgnddPDAbnormal = _mbnRec.PgnddProbDefAbnormal();
  289. assert( pgnddPDAbnormal );
  290. COST costService = _mbnRec.CostService();
  291. // Get beliefs under this state of information
  292. _mbnRec.InferGetBelief( & _gndInfo, _dd );
  293. // Resize and initialize the array of fix/repair sequences
  294. int cStates = _gndInfo.CState();
  295. _vvgndrw.resize( cStates );
  296. for ( int iplan = 0; iplan < cStates; iplan++ )
  297. {
  298. _vvgndrw[iplan].PmbnRec() = & _mbnRec;
  299. }
  300. _bSameSequence = true;
  301. VGPNDDDIST vgndddFixRelevant; // Array of relevant fixable nodes
  302. for ( iplan = 0; iplan < cStates; iplan++ )
  303. {
  304. // If this state is impossible, ignore it
  305. PROB pbPlan = _dd[iplan];
  306. if ( pbPlan == 0.0 )
  307. continue;
  308. #ifdef DUMP
  309. cout << "\nINFOPLAN clamp "
  310. << gndInfo.ZsrefName().Szc()
  311. << " to state = "
  312. << iplan
  313. << ", prob = "
  314. << _dd[iplan];
  315. #endif
  316. // Clamp this info node to this state
  317. CLAMP clamp( true, iplan, true );
  318. _mbnRec.InferEnterEvidence( & _gndInfo, clamp );
  319. // Determine which nodes are relevant given this state of information
  320. _mbnRec.DetermineRelevantFixableNodes( vgndddFixRelevant, true, & _gndInfo );
  321. // If there are no relevant fixables then the configuration is impossible
  322. if ( vgndddFixRelevant.size() == 0 )
  323. continue;
  324. // Collect and sequence the relevant fixable nodes accordingly
  325. _mbnRec.ComputeFixSequence( vgndddFixRelevant, _vvgndrw[iplan] );
  326. // See if this is a new sequence
  327. if ( _bSameSequence )
  328. _bSameSequence = vgndrwFixRepair.BSameSequence( _vvgndrw[iplan] );
  329. }
  330. // Restore the info node to its entry state
  331. _mbnRec.InferEnterEvidence( & _gndInfo, clampInfo );
  332. #ifdef DUMP
  333. cout << "\nINFOPLAN::INFOPLAN: END info node "
  334. << gndInfo.ZsrefName().Szc();
  335. #endif
  336. }
  337. COST INFOPLAN :: Cost ()
  338. {
  339. VPGNODEREFP & vpgndref = _mbnRec.Vpgndref();
  340. int indref = vpgndref.ifind( & _gndInfo );
  341. assert( indref >= 0 );
  342. COST cost = vpgndref[indref]->CostObserve();
  343. ASSERT_THROW( cost != 0.0, EC_INTERNAL_ERROR, "missing observation cost for info node" );
  344. // Rescale the probabilities of each planning state based upon removal of the
  345. // impossible states and renormalization.
  346. PROB pbTotal = 0.0;
  347. for ( int iplan = 0; iplan < _gndInfo.CState(); iplan++ )
  348. {
  349. if ( _vvgndrw[iplan].size() > 0 )
  350. pbTotal += _dd[iplan];
  351. }
  352. assert( pbTotal > 0.0 );
  353. for ( iplan = 0; iplan < _gndInfo.CState(); iplan++ )
  354. {
  355. // Get the rescaled probability of this state of the info node
  356. PROB pbPlan = _dd[iplan];
  357. VGNODERECWORK & vgndrw = _vvgndrw[iplan];
  358. if ( vgndrw.size() == 0 )
  359. {
  360. // The plan is zero length; in other words, no fixables were relevant
  361. // and the plan is impossible
  362. pbPlan = 0.0;
  363. }
  364. pbPlan /= pbTotal;
  365. COST costPlan = _vvgndrw[iplan].Cost();
  366. cost += costPlan * pbPlan;
  367. }
  368. return cost;
  369. }
  370. // Rescale the probabilities for the fix list. This routine sets the
  371. // array bounds to ignore everything from the first unfixable node and beyond.
  372. // Fault probabilities for the list are renormalized against the cumulative
  373. // probability of all the faults in the array. Since there should be no fixable
  374. // nodes of significance after the first unfixable node, the "probLeak" value
  375. // should be very small.
  376. void VGNODERECWORK :: Rescale ()
  377. {
  378. // Accumulate totals of all fault probabilities
  379. PROB probTot = 0.0;
  380. for ( int ind = 0; ind < size(); ind++ )
  381. {
  382. probTot += self[ind].PbFault();
  383. }
  384. PROB probLeak = 1.0; // Renormalized leak (residual) probability
  385. int i1stUnfix = size(); // Index of 1st unfixable node
  386. for ( ind = 0; ind < size(); ind++ )
  387. {
  388. GNODERECWORK & gndrw = self[ind];
  389. if ( ! gndrw.BFixable() )
  390. {
  391. i1stUnfix = ind;
  392. break;
  393. }
  394. //modified to fix the problem
  395. //gndrw.SetPbFault( gndrw.PbFault()/probTot);
  396. PROB pbTemp = gndrw.PbFault();
  397. if(probTot>0.0)
  398. pbTemp /= probTot;
  399. gndrw.SetPbFault( pbTemp );
  400. probLeak -= gndrw.PbFault();
  401. }
  402. ASSERT_THROW( probLeak >= - probTiny,
  403. EC_INTERNAL_ERROR,
  404. "fix/repair recommendations rescaling: residual probability too large" );
  405. #ifdef _DEBUG
  406. // Verify that there are no fixable nodes of signifcance beyond the new end point
  407. int cBeyond = 0;
  408. for ( ; ind < size(); ind++ )
  409. {
  410. GNODERECWORK & gndrw = self[ind];
  411. if ( gndrw.PbFault() < probTiny )
  412. continue; // highly unlikely to be significant
  413. if ( ! gndrw.BFixable() )
  414. continue;
  415. }
  416. assert( cBeyond == 0 );
  417. #endif
  418. // Resize to discard unfixable nodes
  419. resize( i1stUnfix );
  420. }
  421. /////////////////////////////////////////////////////////////////////////////////////////
  422. // VGNODERECWORK::Cost()
  423. //
  424. // purpose:
  425. // calculate cost of a fix sequence (aka ECR(E)), given by
  426. // Cost = Co1 + p1 * Cr1 + (1 - p1) * Co2 + p2 * Cr2 + ... + (1 - sum_i^N pi) Cservice
  427. //
  428. // The 'ielemFirst' argument, if non-zero, is the index of the element to treat as first.
  429. // The 'piMinK' argument, if present, is set to the minimum K value computed.
  430. /////////////////////////////////////////////////////////////////////////////////////////
  431. COST VGNODERECWORK :: Cost (
  432. int ielemFirst, // Element to consider as first in array
  433. int * piMinK ) // Location to store minimum k
  434. {
  435. COST cost = 0.0;
  436. PROB prob = 1.0;
  437. const COST costService = MbnRec().CostService();
  438. COST costK = costService * prob;
  439. assert( _iFixedK == -1 || _iFixedK < size() );
  440. int ielem = 0;
  441. int iMinK = ielemFirst;
  442. const COST costObsProbDef = MbnRec().CostObsProbDef();
  443. int cSize = size();
  444. #ifdef DUMP
  445. cout << "\n\nVGNODERECWORK::Cost("
  446. << ielemFirst
  447. << "), _iFixedK = "
  448. << _iFixedK;
  449. #endif
  450. for ( int iel = 0; iel < cSize; iel++ )
  451. {
  452. // Select the array location, using ielemFirst (if present) as starting point,
  453. // and skipping ielemFirst as necessary later.
  454. ielem = iel == 0
  455. ? ielemFirst
  456. : (iel - (ielemFirst > 0 && iel <= ielemFirst));
  457. // Access the next element in the array
  458. GNODERECWORK & gndrw = self[ielem];
  459. GNODEREFP & gndref = gndrw.Gndref();
  460. // If the node is unfixable, ignore it
  461. if ( ! gndrw.BFixable() )
  462. continue;
  463. const PROB probFault = gndrw.PbFault();
  464. COST costDelta = prob * gndref.CostObserve()
  465. + probFault * (gndref.CostFix() + costObsProbDef);
  466. #ifdef DUMP
  467. cout << "\n\t"
  468. << gndrw;
  469. cout << "\n\t(iel="
  470. << iel
  471. << ",ielem="
  472. << ielem
  473. << ",size="
  474. << cSize
  475. << ")\n\t\tcostDelta("
  476. << costDelta
  477. << ") = prob("
  478. << prob
  479. << ") * costObs("
  480. << gndref.CostObserve()
  481. << ") + probFault("
  482. << probFault
  483. << ") * costFix("
  484. << gndref.CostFix()
  485. << ")"
  486. ;
  487. #endif
  488. cost += costDelta;
  489. prob -= probFault;
  490. // Compute the cost of the sequence if service is inserted here
  491. COST costNow = cost + prob * costService;
  492. #ifdef DUMP
  493. cout << "\n\t\tcostPrior("
  494. << costK
  495. << "), costNow("
  496. << costNow
  497. << ") = cost("
  498. << cost
  499. << ") + prob("
  500. << prob
  501. << ") * costService("
  502. << costService
  503. << "), (prob ="
  504. << prob
  505. << ")";
  506. cout.flush();
  507. #endif
  508. // Were we better off at the last step? Or is K fixed at this point?
  509. if ( costNow < costK || iel == _iFixedK )
  510. {
  511. costK = costNow;
  512. iMinK = ielem;
  513. if ( iel == _iFixedK )
  514. break; // We've reached the fixed point, so stop
  515. }
  516. ASSERT_THROW( prob >= - probTiny,
  517. EC_INTERNAL_ERROR,
  518. "fix/repair recommendations costing: probability underflow" );
  519. }
  520. #ifdef DUMP
  521. cout << "\n\t** ielem="
  522. << ielem
  523. << ", first element = "
  524. << ielemFirst;
  525. if ( _iFixedK < 0 )
  526. cout << ", minimum k = " << iMinK;
  527. else
  528. cout << ", fixed k = " << _iFixedK;
  529. cout << ", cost = "
  530. << costK
  531. << " (residual prob = "
  532. << prob
  533. << ")";
  534. #endif
  535. if ( _iFixedK < 0 )
  536. {
  537. if ( piMinK )
  538. *piMinK = iMinK;
  539. }
  540. return costK;
  541. }
  542. // Set the cost of each node in the sequence
  543. void VGNODERECWORK :: SetSequenceCost ()
  544. {
  545. // Reset any prior minimum fixed K
  546. _iFixedK = -1;
  547. // If "fixPlan", compute the minimum K only on the first cycle,
  548. // then enforce it thereafter.
  549. int iFixedK = -1;
  550. for ( int ind = 0; ind < size(); ind++ )
  551. {
  552. // Compute the cost of the sequence with this node as first
  553. COST cost = Cost( ind, & iFixedK );
  554. // If not "fixplan", reset K for complete search next cycle
  555. if ( MbnRec().ErcMethod() != MBNET_RECOMMENDER::ERCM_FixPlan )
  556. iFixedK = -1;
  557. else
  558. // Else, if first cycle, fix K for remaining cycles.
  559. if ( ind == 0 )
  560. _iFixedK = iFixedK;
  561. self[ind].SetCost( cost );
  562. #ifdef DUMP
  563. cout << "\nSetSequenceCost: "
  564. << self[ind]->Gndd().ZsrefName().Szc()
  565. << " = "
  566. << cost;
  567. #endif
  568. }
  569. _iFixedK = -1;
  570. _bSeqSet = true;
  571. }
  572. bool VGNODERECWORK :: BSameSequence ( const VGNODERECWORK & vgnw )
  573. {
  574. if ( size() != vgnw.size() )
  575. return false;
  576. for ( int ind = 0; ind < size(); ind++ )
  577. {
  578. if ( self[ind].Gndref() != vgnw[ind].Gndref() )
  579. return false;
  580. }
  581. return true;
  582. }
  583. MBNET_RECOMMENDER :: MBNET_RECOMMENDER (
  584. GOBJMBN_CLIQSET & inferEng,
  585. ERCMETHOD ercm )
  586. : MBNET_NODE_RANKER( inferEng.Model() ),
  587. _inferEng( inferEng ),
  588. _propMgr( inferEng.Model() ),
  589. _ercm(ercm),
  590. _err(EC_OK),
  591. _pgnddPDAbnormal(NULL),
  592. _costService(0.0),
  593. _costObsProbDef(0.0),
  594. _bReady(false)
  595. {
  596. }
  597. MBNET_RECOMMENDER :: ~ MBNET_RECOMMENDER ()
  598. {
  599. }
  600. //
  601. // Return true if the network is in a proper state for recommendations
  602. // Note that we don't check whether the network has been expanded or not.
  603. // Since there must already be an inference engine, it's assumed that the
  604. // network is in its correct state.
  605. //
  606. bool MBNET_RECOMMENDER :: BReady ()
  607. {
  608. MODEL::MODELENUM mdlenum( Model() );
  609. _err = EC_OK;
  610. _costService = CostServiceModel();
  611. if ( _costService == 0.0 )
  612. {
  613. _err = EC_VOI_MODEL_COST_FIX;
  614. return false;
  615. }
  616. // Clear the structure
  617. _vpgnddFix.clear(); // Prepare to collect fixable nodes
  618. _vpgndref.clear(); // Clear node reference array
  619. // Iterate over the nodes in the network, checking constraints.
  620. GELEMLNK * pgelm;
  621. GNODEMBND * pgndd;
  622. CLAMP clamp;
  623. int cProbDefSet = 0; // # of instantiated PD nodes
  624. int cFixSetAbnorm = 0; // # of fixables set to "abnormal"
  625. int cInfo = 0; // # of info nodes
  626. int cFixWithParents = 0; // # of fixables with parents
  627. while ( pgelm = mdlenum.PlnkelNext() )
  628. {
  629. // Check only nodes
  630. if ( pgelm->EType() != GOBJMBN::EBNO_NODE )
  631. continue;
  632. // We only support discrete nodes for now
  633. DynCastThrow( pgelm, pgndd );
  634. // See if it has a label
  635. ESTDLBL eLbl = ELbl( *pgndd );
  636. bool bRef = false;
  637. switch ( eLbl )
  638. {
  639. case ESTDLBL_info:
  640. cInfo++;
  641. bRef = true;
  642. break;
  643. case ESTDLBL_problem:
  644. InferGetEvidence( pgndd, clamp );
  645. if ( clamp.BActive() && clamp.Ist() != istNormal )
  646. {
  647. cProbDefSet++; // Problem defining node set abnormal
  648. _pgnddPDAbnormal = pgndd;
  649. PROPMBN * ppropCostObs = _propMgr.PFind( *pgndd, ESTDP_cost_observe );
  650. if ( ppropCostObs )
  651. _costObsProbDef = ppropCostObs->Real();
  652. }
  653. break;
  654. case ESTDLBL_fixobs:
  655. case ESTDLBL_fixunobs:
  656. case ESTDLBL_unfix:
  657. // Collect fixable nodes
  658. _vpgnddFix.push_back( pgndd );
  659. // Check that it's not set abnormal
  660. InferGetEvidence( pgndd, clamp );
  661. if ( clamp.BActive() && clamp.Ist() != istNormal )
  662. cFixSetAbnorm++; // Fixable node set abnormal
  663. bRef = true;
  664. if ( pgndd->CParent() > 0 )
  665. cFixWithParents++; // Fixable node with parents
  666. break;
  667. default:
  668. break;
  669. }
  670. // If necessary, create a reference item for this node
  671. if ( bRef )
  672. {
  673. _vpgndref.push_back( new GNODEREFP( _propMgr, pgndd ) );
  674. }
  675. }
  676. if ( cProbDefSet != 1 )
  677. _err = EC_VOI_PROBDEF_ABNORMAL; // One and only one PD node must be abnormal
  678. else
  679. if ( cFixWithParents > 0 )
  680. _err = EC_VOI_FIXABLE_PARENTS; // Some fixable node(s) has parents
  681. else
  682. if ( cFixSetAbnorm > 0 )
  683. _err = EC_VOI_FIXABLE_ABNORMAL; // No fixable nodes can be abnormal
  684. return _bReady = (_err == EC_OK);
  685. }
  686. // Interface to inference engine
  687. void MBNET_RECOMMENDER :: InferGetBelief ( GNODEMBND * pgndd, MDVCPD & mdvBel )
  688. {
  689. InferEng().GetBelief( pgndd, mdvBel );
  690. }
  691. void MBNET_RECOMMENDER :: InferGetEvidence ( GNODEMBND * pgndd, CLAMP & clamp )
  692. {
  693. InferEng().GetEvidence( pgndd, clamp );
  694. }
  695. void MBNET_RECOMMENDER :: InferEnterEvidence ( GNODEMBND * pgndd, const CLAMP & clamp )
  696. {
  697. InferEng().EnterEvidence( pgndd, clamp );
  698. }
  699. bool MBNET_RECOMMENDER :: BInferImpossible ()
  700. {
  701. return InferEng().BImpossible();
  702. }
  703. void MBNET_RECOMMENDER :: PrintInstantiations ()
  704. {
  705. #ifdef DUMP
  706. GELEMLNK * pgelm;
  707. GNODEMBND * pgndd;
  708. CLAMP clamp;
  709. cout << "\n\tInstantiations:";
  710. MODEL::MODELENUM mdlenum( Model() );
  711. while ( pgelm = mdlenum.PlnkelNext() )
  712. {
  713. // Check only nodes
  714. if ( pgelm->EType() != GOBJMBN::EBNO_NODE )
  715. continue;
  716. // We only support discrete nodes for now
  717. DynCastThrow( pgelm, pgndd );
  718. InferGetEvidence( pgndd, clamp );
  719. if ( clamp.BActive() )
  720. {
  721. cout << "\n\t\tnode "
  722. << pgndd->ZsrefName().Szc()
  723. << " is instantiated to state "
  724. << clamp.Ist()
  725. << ", "
  726. << pgndd->VzsrStates()[clamp.Ist()].Szc();
  727. }
  728. }
  729. cout << "\n\tInstantiations end.";
  730. #endif
  731. }
  732. COST MBNET_RECOMMENDER :: CostServiceModel ()
  733. {
  734. // Get the model's cost-to-fix as service cost.
  735. PROPMBN * ppropFixCost = _propMgr.PFind( ESTDP_cost_fix );
  736. COST costService = ppropFixCost
  737. ? ppropFixCost->Real()
  738. : 0.0;
  739. return costService;
  740. }
  741. // Look up the label property of a node; convert to standard enumeration value.
  742. ESTDLBL MBNET_RECOMMENDER :: ELbl ( GNODEMBN & gnd )
  743. {
  744. PROPMBN * propLbl = PropMgr().PFind( gnd, ESTDP_label );
  745. if ( ! propLbl )
  746. return ESTDLBL_other;
  747. int iUserLbl = propLbl->Real();
  748. int iLbl = PropMgr().IUserToLbl( propLbl->Real() );
  749. return iLbl < 0
  750. ? ESTDLBL_other
  751. : (ESTDLBL) iLbl;
  752. }
  753. // Enter evidence for a troubleshooting model.
  754. //
  755. // If the node is a fixable node being "set" to "normal", uninstantiate all
  756. // information nodes downstream from it.
  757. //
  758. void MBNET_RECOMMENDER :: EnterEvidence (
  759. GNODEMBND * pgndd,
  760. const CLAMP & clamp,
  761. bool bSet )
  762. {
  763. if ( bSet )
  764. {
  765. ESTDLBL eLbl = ELbl( *pgndd );
  766. switch ( eLbl )
  767. {
  768. case ESTDLBL_unfix:
  769. case ESTDLBL_fixobs:
  770. case ESTDLBL_fixunobs:
  771. {
  772. // This is a fixable node
  773. if ( ! clamp.BActive() )
  774. break; // Node is being unset
  775. if ( clamp.Ist() != istNormal )
  776. break; // Node is not being fixed
  777. // Find all downstream information nodes which are instantiated.
  778. VPGNODEMBND vpgndd;
  779. vpgndd.push_back(pgndd);
  780. ExpandDownstream(vpgndd);
  781. CLAMP clampInfo;
  782. for ( int ind = 0; ind < vpgndd.size(); ind++ )
  783. {
  784. GNODEMBND * pgnddInfo = vpgndd[ind];
  785. ESTDLBL l = ELbl( *pgnddInfo );
  786. if ( l != ESTDLBL_info )
  787. continue;
  788. InferGetEvidence( pgnddInfo, clampInfo );
  789. if ( ! clampInfo.BActive() )
  790. continue;
  791. // This is a clamped information node downstream from the fixable
  792. // node being repaired. Unset its instantiation.
  793. InferEnterEvidence( pgnddInfo, CLAMP() );
  794. }
  795. break;
  796. }
  797. default:
  798. break;
  799. }
  800. }
  801. InferEnterEvidence( pgndd, clamp );
  802. }
  803. //
  804. // Compute the probability distribution of the node and compare it to
  805. // the stored distribution. Return true If it has changed.
  806. //
  807. bool MBNET_RECOMMENDER :: BProbsChange ( GPNDDDIST & gpndddist )
  808. {
  809. MDVCPD mdv;
  810. // The the distribution given the current state of evidence
  811. InferGetBelief( gpndddist.Pgnd(), mdv );
  812. // Compare it to the other distribution
  813. MDVCPD & mdvo = gpndddist.Dist();
  814. int cprob = mdvo.first.size();
  815. assert( mdv.first.size() == cprob );
  816. for ( int i = 0; i < cprob; i++ )
  817. {
  818. #ifdef DUMP
  819. cout << "\n\t\tBProbsChange, state = "
  820. << i
  821. << ", old = "
  822. << mdvo[i]
  823. << ", new = "
  824. << mdv[i];
  825. #endif
  826. if ( ! BEqual( mdv[i], mdvo[i] ) )
  827. {
  828. return true;
  829. }
  830. }
  831. return false;
  832. }
  833. // Add to the given array all nodes which are downstream from members
  834. void MBNET_RECOMMENDER :: ExpandDownstream ( VPGNODEMBND & vpgndd )
  835. {
  836. Model().ClearNodeMarks();
  837. // Mark all nodes downstream of every given node
  838. for ( int i = 0; i < vpgndd.size(); i++ )
  839. {
  840. vpgndd[i]->Visit(false);
  841. }
  842. // Collect those nodes
  843. MODEL::MODELENUM mdlenum( Model() );
  844. GELEMLNK * pgelm;
  845. GNODEMBND * pgndd;
  846. while ( pgelm = mdlenum.PlnkelNext() )
  847. {
  848. if ( pgelm->EType() != GOBJMBN::EBNO_NODE )
  849. continue;
  850. // We only support discrete nodes for now
  851. DynCastThrow( pgelm, pgndd );
  852. // Add marked nodes which are not already present
  853. if ( pgndd->IMark() )
  854. {
  855. appendset( vpgndd, pgndd );
  856. }
  857. }
  858. }
  859. void MBNET_RECOMMENDER :: DetermineRelevantFixableNodes (
  860. VGPNDDDIST & vgndddFixRelevant,
  861. bool bUsePriorList,
  862. GNODEMBND * pgnddInfoPlan /* = NULL */ )
  863. {
  864. assert( _vpgnddFix.size() > 0 );
  865. assert( _pgnddPDAbnormal != NULL );
  866. #ifdef DUMP
  867. cout << "\nRecommendations, DetermineRelevantFixableNodes: abnormal PD node is "
  868. << _pgnddPDAbnormal->ZsrefName().Szc();
  869. if ( bUsePriorList )
  870. cout << " (secondary invocation)";
  871. #endif
  872. /*
  873. If 'bUsePriorList' is false:
  874. Find all the relevant fixable nodes; i.e., those fixable nodes which
  875. linked to the Problem node and which are not clamped. If unfixed,
  876. (that is, not repaired and not "unfixable"), accumulate them for a
  877. search of relevant info nodes.
  878. First, visit the problem defining node which is instantiated to an
  879. abnormal state and mark all upstream links to it.
  880. Else, if 'bUsePriorList' is true:
  881. Use the relevant fixable list previously accumulated
  882. */
  883. vgndddFixRelevant.clear(); // clear the result array
  884. int cfix = 0; // count of fixables to search
  885. if ( bUsePriorList )
  886. {
  887. // Use the original list of relevant fixables
  888. cfix = _vgndddFixRelevant.size();
  889. }
  890. else
  891. {
  892. // Fill in a new list of releveant fixables
  893. Model().ClearNodeMarks();
  894. _pgnddPDAbnormal->Visit();
  895. cfix = _vpgnddFix.size();
  896. }
  897. // Accumulate the list of relevant, available (unfixed) fixable nodes, to
  898. // which downstream info nodes will be added
  899. VPGNODEMBND vpgnddDownstreamFromRelevantFixable;
  900. int irel = 0;
  901. for ( int ifix = 0; ifix < cfix; ifix++ )
  902. {
  903. GNODEMBND * pgnddFix;
  904. if ( bUsePriorList )
  905. { // Use prior list element
  906. pgnddFix = _vgndddFixRelevant[ifix].Pgnd();
  907. }
  908. else
  909. { // See if this node was marked by "visit" above
  910. pgnddFix = _vpgnddFix[ifix];
  911. if ( pgnddFix->IMark() == 0 )
  912. continue; // unconnected to current problem
  913. CLAMP clampFix;
  914. InferGetEvidence( pgnddFix, clampFix );
  915. if ( clampFix.BActive() )
  916. continue; // Fixable node has been fixed; irrelevant
  917. }
  918. // This is an unfixed, fixable node involved in the problem;
  919. // append it to the list
  920. vgndddFixRelevant.resize(irel+1);
  921. GPNDDDIST & gpnddd = vgndddFixRelevant[irel++];
  922. gpnddd.Pgnd() = pgnddFix;
  923. // Get its current PD and save it
  924. InferGetBelief( gpnddd.Pgnd(), gpnddd.Dist() );
  925. // If fixable, add it to the list for accumulation of relevant info nodes
  926. ESTDLBL eLbl = ELbl( *pgnddFix );
  927. if ( eLbl == ESTDLBL_fixobs || eLbl == ESTDLBL_fixunobs )
  928. {
  929. vpgnddDownstreamFromRelevantFixable.push_back( pgnddFix );
  930. }
  931. }
  932. #ifdef DUMP
  933. cout << "\n\tInstantiations before relevance check";
  934. PrintInstantiations();
  935. #endif
  936. // Uninstantiate the info nodes which are downstream from any
  937. // RELEVANT UNFIXED fixable nodes. The first step, which is to gather such
  938. // relevant fixable nodes, has been done above.
  939. //
  940. // Note that this is NOT done for the info node being used for INFOPLAN (ECO)
  941. // generation. Since INFOPLAN::INFOPLAN precesses this node through its states,
  942. // it's pointless to uninstantiate it here.
  943. //
  944. // Next, find all info nodes downstream from the relevant unfixed fixables.
  945. // Finally, temporarily rescind the instantiations of those info nodes.
  946. VPNDD_IST vpnddIstReset; // remember pairs of node pointers and ISTs to reset later
  947. // Number of unfixed fixables so far
  948. int cUnfixedNodes = vpgnddDownstreamFromRelevantFixable.size();
  949. // Expand the collection to include all downstream nodes
  950. ExpandDownstream( vpgnddDownstreamFromRelevantFixable );
  951. // Get number of relevant info nodes
  952. int cInfoNodes = vpgnddDownstreamFromRelevantFixable.size() - cUnfixedNodes;
  953. CLAMP clampInfo;
  954. CLAMP clampReset;
  955. int ireset = 0;
  956. #ifdef DUMP
  957. cout << "\n\t"
  958. << cUnfixedNodes
  959. << " fixable nodes are upstream of PD, "
  960. << cInfoNodes
  961. << " nodes are downstream from them";
  962. #endif
  963. for ( int iinfo = cUnfixedNodes;
  964. iinfo < vpgnddDownstreamFromRelevantFixable.size();
  965. iinfo++ )
  966. {
  967. GNODEMBND * pgnddInfo = vpgnddDownstreamFromRelevantFixable[iinfo];
  968. if ( ELbl( *pgnddInfo ) != ESTDLBL_info )
  969. continue; // Not an info node
  970. if ( pgnddInfo == pgnddInfoPlan )
  971. continue; // The info node we're planning for
  972. InferGetEvidence( pgnddInfo, clampInfo );
  973. if ( ! clampInfo.BActive() )
  974. continue; // Not clamped
  975. #ifdef DUMP
  976. cout << "\n\tinfo node "
  977. << pgnddInfo->ZsrefName().Szc()
  978. << " is being unclamped from state "
  979. << clampInfo.Ist();
  980. #endif
  981. // Instantiated info node. Save its ptr and current state for later.
  982. vpnddIstReset.push_back( PNDD_IST( pgnddInfo, clampInfo.Ist() ) );
  983. // Unclamp it for relevance check
  984. InferEnterEvidence( pgnddInfo, clampReset );
  985. }
  986. // Walk the list of relevant fixables accumulated so far and determine those
  987. // which are probabilistically relevant. Move those which are to the front
  988. // of the relevance array, then chop the stragglers off the end.
  989. // Get the current state of the PD node
  990. CLAMP clampProblem;
  991. InferGetEvidence( _pgnddPDAbnormal, clampProblem );
  992. IST istProblemSet = clampProblem.Ist();
  993. #ifdef DUMP
  994. cout << "\n\tInstantiations during relevance check";
  995. PrintInstantiations();
  996. #endif
  997. // Iterate over all open (non-evidenced) states of the problem defining node.
  998. int cNodeFix = vgndddFixRelevant.size();
  999. int cRelevant = 0;
  1000. for ( IST istProblem = 0; istProblem < _pgnddPDAbnormal->CState(); istProblem++ )
  1001. {
  1002. // If we've already stored every possible relevant fixable node, quit
  1003. if ( cRelevant == cUnfixedNodes )
  1004. break;
  1005. // If this is the current problem state, skip it
  1006. if ( istProblem == istProblemSet )
  1007. continue;
  1008. // Temporarily instantiate the PD node to this alternative state
  1009. InferEnterEvidence( _pgnddPDAbnormal, CLAMP(true, istProblem, true) );
  1010. // If state of evidence is impossible, continue
  1011. if ( BInferImpossible() )
  1012. continue;
  1013. // Iterate over the remaining relevant fixable nodes. As they are found to be
  1014. // relevant, the nodes are moved to the front of the array and not checked again.
  1015. for ( int inode = cRelevant; inode < cNodeFix; inode++ )
  1016. {
  1017. GPNDDDIST & gpndddist = vgndddFixRelevant[inode];
  1018. GNODEMBND * pgnddFix = gpndddist.Pgnd();
  1019. CLAMP clampFix;
  1020. InferGetEvidence( pgnddFix, clampFix );
  1021. if ( clampFix.BActive() && clampFix.Ist() == istNormal )
  1022. continue; // This fixable node has been fixed and is irrelevant
  1023. // If the PD of this fixable node changes for this problem instantiation,
  1024. // it's relevant; move it to front of array.
  1025. if ( BProbsChange( gpndddist ) )
  1026. {
  1027. #ifdef DUMP
  1028. cout << "\n\tfixable node "
  1029. << pgnddFix->ZsrefName().Szc()
  1030. << " is probabilistically relevant ";
  1031. #endif
  1032. vswap( vgndddFixRelevant, cRelevant++, inode );
  1033. }
  1034. #ifdef DUMP
  1035. else
  1036. {
  1037. cout << "\n\tfixable node "
  1038. << pgnddFix->ZsrefName().Szc()
  1039. << " is NOT probabilistically relevant ";
  1040. }
  1041. #endif
  1042. }
  1043. }
  1044. // Resize the computed array to chop off the irrelevant nodes
  1045. vgndddFixRelevant.resize( cRelevant );
  1046. // Reset the probdef node back to its current instantiation
  1047. InferEnterEvidence( _pgnddPDAbnormal, clampProblem );
  1048. // Reset the uninstantiated info nodes back to their prior states
  1049. for ( ireset = 0; ireset < vpnddIstReset.size(); ireset++ )
  1050. {
  1051. IST ist = vpnddIstReset[ireset].second;
  1052. GNODEMBND * pgndd = vpnddIstReset[ireset].first;
  1053. CLAMP clampReset(true, ist, true);
  1054. InferEnterEvidence( pgndd, clampReset );
  1055. }
  1056. #ifdef DUMP
  1057. if ( cRelevant )
  1058. {
  1059. cout << "\nRecommendations, DetermineRelevantFixableNodes: relevant fixables are: " ;
  1060. for ( int ifx = 0; ifx < vgndddFixRelevant.size(); ifx++ )
  1061. {
  1062. cout << "\n\tnode "
  1063. << vgndddFixRelevant[ifx].Pgnd()->ZsrefName().Szc()
  1064. << " is relevant fixable #"
  1065. << ifx;
  1066. }
  1067. }
  1068. else
  1069. {
  1070. cout << "\nRecommendations, DetermineRelevantFixableNodes: there are NO relevant fixables " ;
  1071. }
  1072. #endif
  1073. }
  1074. void MBNET_RECOMMENDER :: ComputeFixSequence (
  1075. VGPNDDDIST & vgndddFixRelevant, // IN: Relevant fixable nodes
  1076. VGNODERECWORK & vgnrwFix ) // OUT: Ordered fix/repair sequence
  1077. {
  1078. // Using the array of node references and the array of relevant fixable nodes,
  1079. // initialize the fix/repair sequence array.
  1080. vgnrwFix.resize( vgndddFixRelevant.size() ) ;
  1081. for ( int ind = 0; ind < vgnrwFix.size(); ind++ )
  1082. {
  1083. GNODEMBND * pgndd = vgndddFixRelevant[ind].Pgnd();
  1084. vgnrwFix.InitElem( pgndd, ind );
  1085. }
  1086. VGNODERECWORK::ESORT esort = VGNODERECWORK::ESRT_ProbOverCost;
  1087. switch ( _ercm )
  1088. {
  1089. case ERCM_MostLikely:
  1090. esort = VGNODERECWORK::ESRT_SgnProb;
  1091. break;
  1092. case ERCM_Cheap:
  1093. esort = VGNODERECWORK::ESRT_NegCost;
  1094. break;
  1095. }
  1096. vgnrwFix.Sort( esort );
  1097. vgnrwFix.Rescale();
  1098. #ifdef DUMP
  1099. cout << "\nRecommendations, ComputeFixSequence: fix/repair sequence is:";
  1100. for ( ind = 0; ind < vgnrwFix.size(); ind++ )
  1101. {
  1102. GNODEREFP & gndref = vgnrwFix[ind].Gndref();
  1103. cout << "\n\tnode "
  1104. << ind
  1105. << " is "
  1106. << gndref.Gndd().ZsrefName().Szc()
  1107. << ", p/c = "
  1108. << vgnrwFix[ind].PbOverCost()
  1109. << ", utility = "
  1110. << gndref.Util();
  1111. }
  1112. #endif
  1113. }
  1114. // Identify the relevant info nodes and compute their costs.
  1115. // Formerly "BxComputeCosts()"
  1116. void MBNET_RECOMMENDER :: DetermineRelevantInfoNodes (
  1117. VGNODERECWORK & vgnrwFix,
  1118. VGNODERECWORK & vgnrwInfo )
  1119. {
  1120. assert( _pgnddPDAbnormal != NULL );
  1121. CLAMP clampInfo;
  1122. vgnrwInfo.clear();
  1123. #ifdef DUMP
  1124. cout << "\nRecommendations, DetermineRelevantInfoNodes:";
  1125. #endif
  1126. for ( int ind = 0; ind < _vpgndref.size(); ind++ )
  1127. {
  1128. GNODEREFP * pgndref = _vpgndref[ind];
  1129. assert( pgndref );
  1130. if ( pgndref->ELbl() != ESTDLBL_info )
  1131. continue;
  1132. InferGetEvidence( pgndref->Pgndd(), clampInfo );
  1133. // Instantiated info nodes are irrelevant
  1134. if ( clampInfo.BActive() )
  1135. continue;
  1136. // Create an array of fix/repair plans for all states of this info node
  1137. INFOPLAN infoplan( self, pgndref->Gndd(), vgnrwFix );
  1138. // If all plans result in the same sequence, it's irrelevant
  1139. if ( infoplan.BSameSequence() )
  1140. {
  1141. #ifdef DUMP
  1142. cout << "\n\tinfo node "
  1143. << pgndref->Gndd().ZsrefName().Szc()
  1144. << " is NOT relevant; all plans are the same";
  1145. #endif
  1146. }
  1147. else
  1148. {
  1149. // Add this info node to the array
  1150. vgnrwInfo.InitElem( pgndref->Pgndd() );
  1151. // Set the utility to be the negative of the plan cost
  1152. COST cost = infoplan.Cost();
  1153. pgndref->Util() = - cost;
  1154. #ifdef DUMP
  1155. cout << "\n\tinfo node "
  1156. << pgndref->Gndd().ZsrefName().Szc()
  1157. << " is relevant, utility = "
  1158. << pgndref->Util();
  1159. #endif
  1160. }
  1161. }
  1162. }
  1163. void MBNET_RECOMMENDER :: operator () ()
  1164. {
  1165. // If BReady() has not been called yet, do it now.
  1166. if ( ! _bReady )
  1167. {
  1168. if ( ! BReady() )
  1169. throw GMException( _err, "network state invalid for recommendations" );
  1170. }
  1171. #ifdef DUMP
  1172. cout.precision(8);
  1173. #endif
  1174. // Clear the "ready" flag; i.e., force subsequent call to BReady().
  1175. Unready();
  1176. if ( _ercm != ERCM_FixPlan )
  1177. throw GMException( EC_NYI, "only fix/plan recommendations supported" );
  1178. assert( _pgnddPDAbnormal );
  1179. // Array of fixable nodes
  1180. VGNODERECWORK vgnrwFix( this );
  1181. // Array of informational nodes
  1182. VGNODERECWORK vgnrwInfo( this );
  1183. // Collect the relevant fixable nodes
  1184. DetermineRelevantFixableNodes( _vgndddFixRelevant, false, NULL );
  1185. // Collect and order the relevant fixable node information,
  1186. // sorted according to planning method and rescaled.
  1187. ComputeFixSequence( _vgndddFixRelevant, vgnrwFix );
  1188. // Compute ECR, the expected cost of repair.
  1189. vgnrwFix.SetSequenceCost();
  1190. // If information nodes are relevant, determine the set of them.
  1191. if ( _ercm == ERCM_FixPlan || _ercm == ERCM_FixPlanOnly )
  1192. {
  1193. // Compute ECO, the expected cost of the Observation-Repair sequence.
  1194. DetermineRelevantInfoNodes( vgnrwFix, vgnrwInfo );
  1195. }
  1196. // Collect all relevant fixables and infos and sort them
  1197. VGNODERECWORK vgnrwRecom( this );
  1198. vgnrwRecom.resize( vgnrwFix.size() + vgnrwInfo.size() );
  1199. // Add fixables...
  1200. for ( int ind = 0; ind < vgnrwFix.size(); ind++ )
  1201. {
  1202. vgnrwRecom[ind] = vgnrwFix[ind];
  1203. }
  1204. // Add infos...
  1205. int indStart = ind;
  1206. for ( ind = 0; ind < vgnrwInfo.size(); ind++ )
  1207. {
  1208. vgnrwRecom[indStart + ind] = vgnrwInfo[ind];
  1209. }
  1210. // Sort by negative utility
  1211. vgnrwRecom.Sort( VGNODERECWORK::ESRT_SgnUtil );
  1212. // Copy information to the output areas, ordered by lowest cost.
  1213. // First, determine how many are more expensive than a service call
  1214. // since we discard those.
  1215. int cRecom = vgnrwRecom.size();
  1216. int iRecom = 0;
  1217. if ( _costService != 0.0 )
  1218. {
  1219. for ( iRecom = 0; iRecom < cRecom; iRecom++ )
  1220. {
  1221. COST cost = vgnrwRecom[iRecom].Gndref().Util();
  1222. if ( cost >= _costService )
  1223. break;
  1224. }
  1225. cRecom = iRecom;
  1226. }
  1227. _vzsrNodes.resize(cRecom);
  1228. _vlrValues.resize(cRecom);
  1229. for ( iRecom = 0; iRecom < cRecom; iRecom++ )
  1230. {
  1231. GNODEREFP & gndref = vgnrwRecom[iRecom].Gndref();
  1232. // Add the node name to the list
  1233. _vzsrNodes[iRecom] = gndref.Gndd().ZsrefName();
  1234. // and give its score (utility)
  1235. _vlrValues[iRecom] = gndref.Util();
  1236. #ifdef DUMP
  1237. cout << "\nRecommendation # "
  1238. << iRecom
  1239. << ", node "
  1240. << _vzsrNodes[iRecom].Szc()
  1241. << " = "
  1242. << _vlrValues[iRecom];
  1243. cout.flush();
  1244. #endif
  1245. }
  1246. }