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.

1710 lines
41 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1997 - 1998
  6. //
  7. // File: clique.cpp
  8. //
  9. //--------------------------------------------------------------------------
  10. //
  11. // clique.cpp
  12. //
  13. #include <basetsd.h>
  14. #include "cliqset.h"
  15. #include "clique.h"
  16. #include "cliqwork.h"
  17. #include "parmio.h"
  18. #ifdef _DEBUG // In debug mode only...
  19. #define CONSISTENCY // Do complete consistency checking on sepsets
  20. // #define DUMP // Perform general dumping of objects
  21. // #define DUMPCLIQUESET // Dump extensive tables from clique tree
  22. // #define INFERINIT // Full initial tree balancing
  23. #endif
  24. ////////////////////////////////////////////////////////
  25. ////////////////////////////////////////////////////////
  26. // GEDGEMBN_CLIQ: Edges between cliques and member nodes
  27. ////////////////////////////////////////////////////////
  28. ////////////////////////////////////////////////////////
  29. GEDGEMBN_CLIQ :: GEDGEMBN_CLIQ (
  30. GOBJMBN_CLIQUE * pgnSource,
  31. GNODEMBN * pgndSink,
  32. int iFcqlRole )
  33. : GEDGEMBN( pgnSource, pgndSink ),
  34. _iFcqlRole( iFcqlRole ),
  35. _iMark( pgndSink->IMark() ),
  36. _bBuilt( false )
  37. {
  38. }
  39. void GEDGEMBN_CLIQ :: Build ()
  40. {
  41. if ( ! BBuilt() )
  42. {
  43. GNODEMBND * pgndd;
  44. DynCastThrow( PgndSink(), pgndd );
  45. // If role is "Family", this edge is used for marginalization of belief
  46. // and creating joint distribution in clique
  47. if ( BFamily() )
  48. {
  49. ReorderFamily( pgndd, _vimdFamilyReorder );
  50. // Build the reordered marginals table for the node
  51. MargCpd().CreateOrderedCPDFromNode( pgndd, _vimdFamilyReorder );
  52. // Build an iterator between the CPD and the clique joint
  53. MiterLoadClique().Build( PclqParent()->Marginals(), MargCpd() );
  54. // Build the belief marginalization structure
  55. MiterNodeBelief().Build( PclqParent()->Marginals(), pgndd );
  56. }
  57. _bBuilt = true;
  58. }
  59. }
  60. void GEDGEMBN_CLIQ :: LoadCliqueFromNode ()
  61. {
  62. assert( _bBuilt );
  63. MiterLoadClique().MultiplyBy( MargCpd() );
  64. }
  65. GEDGEMBN_CLIQ :: ~ GEDGEMBN_CLIQ()
  66. {
  67. }
  68. GOBJMBN_CLIQUE * GEDGEMBN_CLIQ :: PclqParent()
  69. {
  70. GOBJMBN * pobj = PobjSource();
  71. GOBJMBN_CLIQUE * pclq;
  72. DynCastThrow( pobj, pclq );
  73. return pclq;
  74. }
  75. GNODEMBN * GEDGEMBN_CLIQ :: PgndSink()
  76. {
  77. GOBJMBN * pobj = PobjSink();
  78. GNODEMBN * pgnd;
  79. DynCastThrow( pobj, pgnd );
  80. return pgnd;
  81. }
  82. // Using the topological renumber of the nodes, produce
  83. // an array correlating the old family to the new order.
  84. // In other words, vimd[0] will be the family index of
  85. // the node which had the lowest topological order; vimd[1]
  86. // will be the family index of the next lowest, etc.
  87. //
  88. // Note that node itself is always last in either ordering.
  89. void GEDGEMBN_CLIQ :: ReorderFamily ( GNODEMBN * pgnd, VIMD & vimd )
  90. {
  91. VPGNODEMBN vpgndFamily;
  92. // Get the family (parents & self)
  93. pgnd->GetFamily( vpgndFamily );
  94. int cFam = vpgndFamily.size();
  95. vimd.resize( cFam );
  96. for ( int i = 0; i < cFam; i++ )
  97. {
  98. int iLow = INT_MAX;
  99. int iFam = INT_MAX;
  100. // Find the lowest unrecorded family member
  101. for ( int j = 0; j < cFam; j++ )
  102. {
  103. GNODEMBN * pgndFam = vpgndFamily[j];
  104. if ( pgndFam == NULL )
  105. continue;
  106. if ( pgndFam->IMark() < iLow )
  107. {
  108. iLow = pgndFam->IMark();
  109. iFam = j;
  110. }
  111. }
  112. assert( iLow != INT_MAX );
  113. vimd[i] = iFam;
  114. vpgndFamily[iFam] = NULL;
  115. }
  116. }
  117. ////////////////////////////////////////////////////////
  118. ////////////////////////////////////////////////////////
  119. // GEDGEMBN_SEPSET: A separator marginal
  120. ////////////////////////////////////////////////////////
  121. ////////////////////////////////////////////////////////
  122. GEDGEMBN_SEPSET :: GEDGEMBN_SEPSET (
  123. GOBJMBN_CLIQUE * pgnSource,
  124. GOBJMBN_CLIQUE * pgnSink )
  125. : GEDGEMBN( pgnSource, pgnSink ),
  126. _pmargOld( new MARGINALS ),
  127. _pmargNew( new MARGINALS )
  128. {
  129. }
  130. GEDGEMBN_SEPSET :: ~ GEDGEMBN_SEPSET()
  131. {
  132. delete _pmargOld;
  133. delete _pmargNew;
  134. }
  135. void GEDGEMBN_SEPSET :: ExchangeMarginals ()
  136. {
  137. pexchange( _pmargOld, _pmargNew );
  138. }
  139. GOBJMBN_CLIQUE * GEDGEMBN_SEPSET :: PclqParent()
  140. {
  141. GOBJMBN * pobj = PobjSource();
  142. GOBJMBN_CLIQUE * pclq;
  143. DynCastThrow( pobj, pclq );
  144. return pclq;
  145. }
  146. GOBJMBN_CLIQUE * GEDGEMBN_SEPSET :: PclqChild()
  147. {
  148. GOBJMBN * pobj = PobjSink();
  149. GOBJMBN_CLIQUE * pclq;
  150. DynCastThrow( pobj, pclq );
  151. return pclq;
  152. }
  153. void GEDGEMBN_SEPSET :: GetMembers ( VPGNODEMBN & vpgnode )
  154. {
  155. GOBJMBN_CLIQUE * pclqSource = PclqParent();
  156. GOBJMBN_CLIQUE * pclqSink = PclqChild();
  157. VPGNODEMBN vpgndSink;
  158. VPGNODEMBN vpgndSource;
  159. pclqSource->GetMembers( vpgndSource );
  160. pclqSink->GetMembers( vpgndSink );
  161. assert( vpgndSink.size() > 0 );
  162. assert( vpgndSource.size() > 0 );
  163. // Fill the given array with the intersection of the two clique
  164. // member node arrays. Since we cannot sort them into cliqing order
  165. // anymore (IMark() is unreliable after cliquing), we just search
  166. // one against the other in order to guarantee that the intersection
  167. // result set has the same node ordering as the original sets.
  168. int ibLast = -1;
  169. for ( int ia = 0; ia < vpgndSink.size(); ia++ )
  170. {
  171. GNODEMBN * pa = vpgndSink[ia];
  172. for ( int ib = ibLast+1; ib < vpgndSource.size(); ib++ )
  173. {
  174. GNODEMBN * pb = vpgndSource[ib];
  175. if ( pa == pb )
  176. {
  177. vpgnode.push_back(pa);
  178. ibLast = ib;
  179. break;
  180. }
  181. }
  182. }
  183. #ifdef DUMP
  184. if ( vpgnode.size() == 0 )
  185. {
  186. cout << "\nSEPSET INTERSECTION NULL: source clique:";
  187. pclqSource->Dump();
  188. cout << "\n\t\tsink clique:";
  189. pclqSink->Dump();
  190. cout << "\n";
  191. cout.flush();
  192. }
  193. #endif
  194. assert( vpgnode.size() > 0 );
  195. }
  196. void GEDGEMBN_SEPSET :: CreateMarginals ()
  197. {
  198. VPGNODEMBN vpgnd;
  199. GetMembers( vpgnd );
  200. MarginalsOld().Init( vpgnd );
  201. MarginalsNew().Init( vpgnd );
  202. }
  203. void GEDGEMBN_SEPSET :: InitMarginals ()
  204. {
  205. assert( VerifyMarginals() );
  206. MarginalsOld().Clear( 1.0 );
  207. MarginalsNew().Clear( 1.0 );
  208. if ( ! _miterParent.BBuilt() )
  209. _miterParent.Build( PclqParent()->Marginals(), MarginalsOld() );
  210. if ( ! _miterChild.BBuilt() )
  211. _miterChild.Build( PclqChild()->Marginals(), MarginalsOld() );
  212. }
  213. bool GEDGEMBN_SEPSET :: VerifyMarginals ()
  214. {
  215. VPGNODEMBN vpgnd;
  216. GetMembers( vpgnd );
  217. VIMD vimd = MARGINALS::VimdFromVpgnd( vpgnd );
  218. return vimd == Marginals().Vimd();
  219. }
  220. void GEDGEMBN_SEPSET :: UpdateRatios ()
  221. {
  222. MarginalsOld().UpdateRatios( MarginalsNew() );
  223. }
  224. void GEDGEMBN_SEPSET :: AbsorbClique ( bool bFromParentToChild )
  225. {
  226. MARGSUBITER * pmiterFrom;
  227. MARGSUBITER * pmiterTo;
  228. if ( bFromParentToChild )
  229. {
  230. pmiterFrom = & _miterParent;
  231. pmiterTo = & _miterChild;
  232. }
  233. else
  234. {
  235. pmiterFrom = & _miterChild;
  236. pmiterTo = & _miterParent;
  237. }
  238. // Marginalize "from" probs into the "new" marginals table
  239. pmiterFrom->MarginalizeInto( MarginalsNew() );
  240. // Absorb the changes into the "old" marginals table
  241. UpdateRatios();
  242. // Multiply the table into the "to"'s marginals
  243. pmiterTo->MultiplyBy( MarginalsOld() );
  244. // Finally, exchange the marginals tables
  245. ExchangeMarginals();
  246. }
  247. void GEDGEMBN_SEPSET :: BalanceCliquesCollect ()
  248. {
  249. // Use the "new" table as a work area.
  250. // Marginalize the child into the work area
  251. _miterChild.MarginalizeInto( MarginalsNew() );
  252. // Update the parent with those values
  253. _miterParent.MultiplyBy( MarginalsNew() );
  254. // Invert each value, so we're really dividing
  255. MarginalsNew().Invert();
  256. // Update the child marginals by dividing by the marginals
  257. _miterChild.MultiplyBy( MarginalsNew() );
  258. // Clear the "new" marginals back to 1.0.
  259. MarginalsNew().Clear( 1.0 );
  260. }
  261. void GEDGEMBN_SEPSET :: BalanceCliquesDistribute ()
  262. {
  263. // Set the old marginals to the parent clique's values
  264. _miterParent.MarginalizeInto( MarginalsOld() );
  265. // Update the child marginals by those values
  266. _miterChild.MultiplyBy( MarginalsOld() );
  267. // "Old" marginals are left as they are
  268. }
  269. void GEDGEMBN_SEPSET :: UpdateParentClique ()
  270. {
  271. AbsorbClique( false );
  272. }
  273. void GEDGEMBN_SEPSET :: UpdateChildClique ()
  274. {
  275. AbsorbClique( true );
  276. }
  277. void GEDGEMBN_SEPSET :: Dump ()
  278. {
  279. GOBJMBN_CLIQUE * pclqParent = PclqParent();
  280. GOBJMBN_CLIQUE * pclqChild = PclqChild();
  281. cout << "\n=== Sepset between parent clique "
  282. << pclqParent->IClique()
  283. << " and child clique "
  284. << pclqChild->IClique()
  285. << ", \n\n\tOld marginals:";
  286. _pmargOld->Dump();
  287. cout << "\n\n\tNew marginals:";
  288. _pmargNew->Dump();
  289. cout << "\n\n";
  290. }
  291. bool GEDGEMBN_SEPSET :: BConsistent ()
  292. {
  293. // Get the sepset member list for creation of temporary marginals
  294. VPGNODEMBN vpgnd;
  295. GetMembers( vpgnd );
  296. // Create the marginals for the parent clique
  297. GOBJMBN_CLIQUE * pclqParent = PclqParent();
  298. MARGINALS margParent;
  299. margParent.Init( vpgnd );
  300. pclqParent->Marginals().Marginalize( margParent );
  301. // Create the marginals for the child clique
  302. GOBJMBN_CLIQUE * pclqChild = PclqChild();
  303. MARGINALS margChild;
  304. margChild.Init( vpgnd );
  305. pclqChild->Marginals().Marginalize( margChild );
  306. // Are they equivalent?
  307. bool bOK = margParent.BEquivalent( margChild, 0.00000001 );
  308. #ifdef DUMP
  309. if ( ! bOK )
  310. {
  311. cout << "\nGEDGEMBN_SEPSET::BConsistent: cliques are NOT consistent, parent clique "
  312. << pclqParent->IClique()
  313. << ", child "
  314. << pclqChild->IClique();
  315. MARGINALS::Iterator itParent(margParent);
  316. MARGINALS::Iterator itChild(margChild);
  317. cout << "\n\tparent marginals: "
  318. << itParent;
  319. cout << "\n\tchild marginals: "
  320. << itChild
  321. << "\n";
  322. cout.flush();
  323. }
  324. #endif
  325. #ifdef NEVER
  326. MARGINALS margParent2;
  327. margParent2.Init( vpgnd );
  328. _miterParent.Test( margParent2 );
  329. _miterParent.MarginalizeInto( margParent2 );
  330. bOK = margParent.BEquivalent( margParent2, 0.00000001 );
  331. #endif
  332. ASSERT_THROW( bOK, EC_INTERNAL_ERROR, "inconsistent cliques" );
  333. return bOK;
  334. }
  335. ////////////////////////////////////////////////////////
  336. ////////////////////////////////////////////////////////
  337. // GOBJMBN_CLIQUE: A Clique
  338. ////////////////////////////////////////////////////////
  339. ////////////////////////////////////////////////////////
  340. GOBJMBN_CLIQUE :: GOBJMBN_CLIQUE (
  341. int iClique,
  342. int iInferEngID )
  343. : _iClique( iClique ),
  344. _iInferEngID( iInferEngID ),
  345. _bRoot(false),
  346. _bCollect(false)
  347. {
  348. }
  349. GOBJMBN_CLIQUE :: ~ GOBJMBN_CLIQUE()
  350. {
  351. }
  352. // Initialize a clique by finding all edges leading from "family"
  353. // arcs and initializing the marginals from there.
  354. void GOBJMBN_CLIQUE :: LoadMarginals ()
  355. {
  356. GNODEMBND * pgnddSink;
  357. GEDGEMBN_CLIQ * pgedgeMbr;
  358. // Prepare to enumerate child member arcs
  359. GNODENUM<GOBJMBN> benumMembers(false);
  360. benumMembers.SetETypeFollow( GEDGEMBN::ETCLIQUE );
  361. // Enumerate child member arcs, reloading the marginals for nodes for which this
  362. // clique is their "self" clique.
  363. for ( benumMembers.Set( this );
  364. benumMembers.PnodeCurrent();
  365. benumMembers++ )
  366. {
  367. DynCastThrow( benumMembers.PgedgeCurrent(), pgedgeMbr );
  368. pgedgeMbr->Build();
  369. if ( pgedgeMbr->BFamily() )
  370. pgedgeMbr->LoadCliqueFromNode();
  371. }
  372. // Enumerate child member arcs, entering evidence (clamped state) for nodes for which this
  373. // clique is their "self"
  374. for ( benumMembers.Set( this );
  375. benumMembers.PnodeCurrent();
  376. benumMembers++ )
  377. {
  378. DynCastThrow( benumMembers.PgedgeCurrent(), pgedgeMbr );
  379. if ( ! pgedgeMbr->BSelf() )
  380. continue;
  381. DynCastThrow( benumMembers.PnodeCurrent(), pgnddSink );
  382. // Note: ClampNode is benign when node is unclamped.
  383. Marginals().ClampNode( pgnddSink, pgedgeMbr->Clamp() );
  384. }
  385. SetCollect();
  386. }
  387. void GOBJMBN_CLIQUE :: GetMembers ( VPGNODEMBN & vpgnode )
  388. {
  389. GNODENUM<GOBJMBN> benumMembers(false);
  390. benumMembers.SetETypeFollow( GEDGEMBN::ETCLIQUE );
  391. for ( benumMembers.Set( this );
  392. benumMembers.PnodeCurrent();
  393. benumMembers++ )
  394. {
  395. GOBJMBN * pgobj = *benumMembers;
  396. GNODEMBN * pgnd;
  397. DynCastThrow( pgobj, pgnd );
  398. vpgnode.push_back( pgnd );
  399. }
  400. assert( vpgnode.size() > 0 );
  401. }
  402. void GOBJMBN_CLIQUE :: CreateMarginals ()
  403. {
  404. VPGNODEMBN vpgnd;
  405. GetMembers( vpgnd );
  406. Marginals().Init( vpgnd );
  407. }
  408. void GOBJMBN_CLIQUE :: InitMarginals ()
  409. {
  410. assert( VerifyMarginals() );
  411. Marginals().Clear( 1.0 );
  412. }
  413. bool GOBJMBN_CLIQUE :: VerifyMarginals ()
  414. {
  415. VPGNODEMBN vpgnd;
  416. GetMembers( vpgnd );
  417. VIMD vimd = MARGINALS::VimdFromVpgnd( vpgnd );
  418. return vimd == Marginals().Vimd();
  419. }
  420. void GOBJMBN_CLIQUE :: Dump ()
  421. {
  422. cout << "\n=== Clique "
  423. << _iClique
  424. << ", tree ID: "
  425. << _iInferEngID
  426. << ", root = "
  427. << _bRoot;
  428. _marg.Dump();
  429. cout << "\n\n";
  430. }
  431. void GOBJMBN_CLIQUE :: GetBelief ( GNODEMBN * pgnd, MDVCPD & mdvBel )
  432. {
  433. GNODEMBND * pgndd;
  434. DynCastThrow( pgnd, pgndd );
  435. Marginals().Marginalize( pgndd, mdvBel );
  436. }
  437. ////////////////////////////////////////////////////////
  438. ////////////////////////////////////////////////////////
  439. // GOBJMBN_CLIQSET: The graphical model junction tree
  440. ////////////////////////////////////////////////////////
  441. ////////////////////////////////////////////////////////
  442. GOBJMBN_CLIQSET :: GOBJMBN_CLIQSET (
  443. MBNET & model,
  444. REAL rMaxEstimatedSize,
  445. int iInferEngID )
  446. : GOBJMBN_INFER_ENGINE( model, rMaxEstimatedSize, iInferEngID )
  447. {
  448. Clear() ;
  449. }
  450. void GOBJMBN_CLIQSET :: Clear ()
  451. {
  452. _eState = CTOR;
  453. _cCliques = 0;
  454. _cCliqueMemberArcs = 0;
  455. _cSepsetArcs = 0;
  456. _cUndirArcs = 0;
  457. _probNorm = 1.0;
  458. _bReset = true;
  459. _bCollect = true;
  460. _cqsetStat.Clear();
  461. };
  462. GOBJMBN_CLIQSET :: ~ GOBJMBN_CLIQSET ()
  463. {
  464. #ifdef DUMP
  465. Dump();
  466. #endif
  467. Destroy();
  468. }
  469. bool GOBJMBN_CLIQSET :: BImpossible ()
  470. {
  471. return ProbNorm() == 0.0;
  472. }
  473. // Add an undirected arc iff there isn't one already.
  474. bool GOBJMBN_CLIQSET :: BAddUndirArc ( GNODEMBN * pgndbnSource, GNODEMBN * pgndbnSink )
  475. {
  476. if ( pgndbnSource->BIsNeighbor( pgndbnSink ) )
  477. return false;
  478. #ifdef DUMP
  479. cout << "\n\t\tADD undirected arc from "
  480. << pgndbnSource->ZsrefName().Szc()
  481. << " to "
  482. << pgndbnSink->ZsrefName().Szc();
  483. #endif
  484. Model().AddElem( new GEDGEMBN_U( pgndbnSource, pgndbnSink ) );
  485. ++_cUndirArcs;
  486. return true;
  487. }
  488. void GOBJMBN_CLIQSET :: CreateUndirectedGraph ( bool bMarryParents )
  489. {
  490. if ( EState() >= MORAL )
  491. return;
  492. int cDirArcs = 0;
  493. int cUndirArcs = 0;
  494. int cNodes = 0;
  495. GELEMLNK * pgelm;
  496. #ifdef DUMP
  497. cout << "\n\n***** MORALIZE GRAPH";
  498. #endif
  499. if ( EState() < MORAL )
  500. {
  501. // Create an undirected arc for every directed arc.
  502. MODEL::MODELENUM mdlenum( Model() );
  503. while ( pgelm = mdlenum.PlnkelNext() )
  504. {
  505. // Check that it's an edge
  506. if ( ! pgelm->BIsEType( GELEM::EGELM_EDGE ) )
  507. continue;
  508. // Check that it's a directed probabilistic arc
  509. if ( pgelm->EType() != GEDGEMBN::ETPROB )
  510. continue;
  511. GEDGEMBN * pgedge;
  512. DynCastThrow( pgelm, pgedge );
  513. GNODEMBN * pgndbnSource;
  514. GNODEMBN * pgndbnSink;
  515. DynCastThrow( pgedge->PnodeSource(), pgndbnSource );
  516. DynCastThrow( pgedge->PnodeSink(), pgndbnSink );
  517. // If the sink (child) node has been expanded,
  518. // consider only Expansion parents
  519. if ( pgndbnSink->BFlag( EIBF_Expanded )
  520. && ! pgndbnSource->BFlag( EIBF_Expansion ) )
  521. continue;
  522. cDirArcs++;
  523. cUndirArcs += BAddUndirArc( pgndbnSource, pgndbnSink );
  524. }
  525. assert( cDirArcs == cUndirArcs ) ;
  526. // Undirected graph has been created
  527. _eState = UNDIR;
  528. }
  529. if ( !bMarryParents )
  530. return;
  531. #ifdef DUMP
  532. cout << "\n\n***** MARRY PARENTS";
  533. #endif
  534. MODEL::MODELENUM mdlenum( Model() );
  535. GNODENUM<GNODEMBN> benumparent(true);
  536. benumparent.SetETypeFollow( GEDGEMBN::ETPROB );
  537. GNODEMBN * pgndmbn;
  538. VPGNODEMBN vpgnd;
  539. while ( pgelm = mdlenum.PlnkelNext() )
  540. {
  541. if ( pgelm->EType() != EBNO_NODE )
  542. continue;
  543. DynCastThrow( pgelm, pgndmbn );
  544. // Collect the parents
  545. vpgnd.resize(0);
  546. pgndmbn->GetParents( vpgnd );
  547. // Marry them
  548. int cParent = vpgnd.size();
  549. for ( int iParent = 0; iParent < cParent - 1; iParent++ )
  550. {
  551. for ( int ip2 = iParent+1; ip2 < cParent ; ip2++ )
  552. {
  553. BAddUndirArc( vpgnd[iParent], vpgnd[ip2] );
  554. }
  555. }
  556. }
  557. // Graph is now moral
  558. _eState = MORAL;
  559. }
  560. //
  561. // Return the number of neighbors of this node which are unlinked
  562. //
  563. int GOBJMBN_CLIQSET :: CNeighborUnlinked ( GNODEMBN * pgndmbn, bool bLinkNeighbors )
  564. {
  565. int cNeighborUnlinked = 0;
  566. // Get the array of neighbors
  567. VPGNODEMBN vpgnode;
  568. pgndmbn->GetNeighbors( vpgnode );
  569. #ifdef DUMP
  570. cout << "\n\t\tCNeighborUnlinked, called for node "
  571. << pgndmbn->ZsrefName().Szc();
  572. #endif
  573. for ( int inbor = 0; inbor < vpgnode.size(); inbor++ )
  574. {
  575. GNODEMBN * pgndNbor = vpgnode[inbor];
  576. #ifdef DUMP
  577. cout << "\n\t\t\t" << pgndNbor->ZsrefName().Szc();
  578. int cUnlinked = 0;
  579. #endif
  580. if ( pgndNbor->IMark() )
  581. continue; // Node has been eliminated already
  582. // Check it against all other neighbors.
  583. for ( int inbor2 = inbor + 1; inbor2 < vpgnode.size(); inbor2++ )
  584. {
  585. GNODEMBN * pgndNbor2 = vpgnode[inbor2];
  586. // See if node has been eliminated already or is already a neighbor
  587. if ( pgndNbor2->IMark() )
  588. continue;
  589. if ( pgndNbor->BIsNeighbor( pgndNbor2 ) )
  590. {
  591. assert( pgndNbor2->BIsNeighbor( pgndNbor ) );
  592. continue;
  593. }
  594. #ifdef DUMP
  595. cUnlinked++;
  596. #endif
  597. ++cNeighborUnlinked;
  598. if ( bLinkNeighbors )
  599. {
  600. BAddUndirArc( pgndNbor, pgndNbor2 );
  601. #ifdef DUMP
  602. cout << " ("
  603. << pgndNbor->ZsrefName().Szc()
  604. << " <-> "
  605. << pgndNbor2->ZsrefName().Szc()
  606. << ") ";
  607. #endif
  608. }
  609. }
  610. #ifdef DUMP
  611. if ( cUnlinked )
  612. cout << " <-- unlinked to "
  613. << cUnlinked
  614. << " neighbors";
  615. #endif
  616. }
  617. #ifdef DUMP
  618. cout << "\n\t\t---- total unlinked = " << cNeighborUnlinked;
  619. #endif
  620. return cNeighborUnlinked;
  621. }
  622. void GOBJMBN_CLIQSET :: Eliminate ( GNODEMBN * pgndmbn, CLIQSETWORK & clqsetWork )
  623. {
  624. #ifdef DUMP
  625. cout << "\n\n***** ELIMINATE "
  626. << pgndmbn->ZsrefName().Szc();
  627. #endif
  628. // Add another array to the clique set and fill it with the clique menbers
  629. clqsetWork._vvpgnd.push_back( VPGNODEMBN() );
  630. VPGNODEMBN & vpgndClique = clqsetWork._vvpgnd[ clqsetWork._vvpgnd.size() - 1 ];
  631. // Complete the elimination of this node and its neighbors.
  632. CNeighborUnlinked( pgndmbn, true );
  633. pgndmbn->IMark() = ++clqsetWork._iElimIndex;
  634. // Start the clique with this entry.
  635. vpgndClique.push_back( pgndmbn );
  636. // Iterate over the neighbors, adding the unmarked ones
  637. GNODENUM_UNDIR gnenumUndir;
  638. for ( gnenumUndir = pgndmbn;
  639. gnenumUndir.PnodeCurrent();
  640. gnenumUndir++ )
  641. {
  642. GNODEMBN * pgndmbNeighbor = *gnenumUndir;
  643. if ( pgndmbNeighbor->IMark() == 0 )
  644. vpgndClique.push_back( pgndmbNeighbor );
  645. }
  646. #ifdef DUMP
  647. cout << "\n\t\tNEW CLIQUE: ";
  648. clqsetWork.DumpClique( clqsetWork._vvpgnd.size() - 1 );
  649. #endif
  650. assert( pgndmbn->IMark() > 0 );
  651. }
  652. void GOBJMBN_CLIQSET :: GenerateCliques ( CLIQSETWORK & clqsetWork )
  653. {
  654. // Reset marks in all nodes
  655. Model().ClearNodeMarks();
  656. clqsetWork._vvpgnd.clear();
  657. #ifdef DUMP
  658. cout << "\n\n***** GENERATE CLIQUES";
  659. #endif
  660. for(;;)
  661. {
  662. // Find the node that requires the fewest edges to turn into a clique.
  663. GNODEMBN * pgndmbnMin = NULL;
  664. int cNeighborMin = INT_MAX;
  665. MODEL::MODELENUM mdlenum( Model() );
  666. GELEMLNK * pgelm;
  667. while ( pgelm = mdlenum.PlnkelNext() )
  668. {
  669. if ( pgelm->EType() != EBNO_NODE )
  670. continue;
  671. GNODEMBN * pgndmbn;
  672. DynCastThrow( pgelm, pgndmbn );
  673. if ( pgndmbn->IMark() )
  674. continue; // Node has been eliminated already
  675. int cNeighborUnlinked = CNeighborUnlinked( pgndmbn );
  676. if ( cNeighborMin > cNeighborUnlinked )
  677. {
  678. pgndmbnMin = pgndmbn;
  679. if ( (cNeighborMin = cNeighborUnlinked) == 0 )
  680. break; // zero is as few neighbors as possible
  681. }
  682. }
  683. if ( pgndmbnMin == NULL )
  684. break;
  685. // Mark the node for elimination and assign an elimination order to it. This
  686. // number is crucial for the construction of the strong junction tree.
  687. #ifdef DUMP
  688. cout << "\nGenerateCliques: Eliminate "
  689. << pgndmbnMin->ZsrefName().Szc()
  690. << ", which has "
  691. << cNeighborMin
  692. << " unlinked neighbors";
  693. #endif
  694. Eliminate( pgndmbnMin, clqsetWork );
  695. }
  696. #ifdef DUMP
  697. cout << "\n\n";
  698. #endif
  699. }
  700. //
  701. // Create the junction tree.
  702. //
  703. void GOBJMBN_CLIQSET :: Create ()
  704. {
  705. Model().CreateTopology();
  706. ASSERT_THROW( EState() == CTOR, EC_INTERNAL_ERROR, "GOBJMBN_CLIQSET:Create already called" );
  707. // If it hasn't been done already, create the undirected graph and moralize it.
  708. CreateUndirectedGraph(true);
  709. CLIQSETWORK clqsetWork(self);
  710. clqsetWork._iElimIndex = 1;
  711. // Triangulate the undirected graph, eliminating nodes and accumulating cliques
  712. // along the way.
  713. GenerateCliques( clqsetWork );
  714. if ( clqsetWork._vvpgnd.size() == 0 )
  715. return;
  716. _eState = CLIQUED;
  717. #ifdef DUMP
  718. clqsetWork.DumpCliques();
  719. #endif
  720. // Provide a total ordering over the nodes based upon topological level
  721. // MSRDEVBUG: What happened to the elimination index? Koos doesn't use it; will we?
  722. // Renumbering here overwrites the elimination order.
  723. clqsetWork.RenumberNodesForCliquing();
  724. // Build the cliques
  725. clqsetWork.BuildCliques();
  726. // Set clique membership and topological information
  727. clqsetWork.SetTopologicalInfo();
  728. // Check that the running intersection property holds
  729. ASSERT_THROW( clqsetWork.BCheckRIP(),
  730. EC_INTERNAL_ERROR,
  731. "GOBJMBN_CLIQSET::Create: junction tree failed RIP test" );
  732. // See if the resulting memory allocation size would violate the size estimate
  733. if ( _rEstMaxSize > 0.0 )
  734. {
  735. REAL rSizeEstimate = clqsetWork.REstimatedSize();
  736. if ( rSizeEstimate > _rEstMaxSize )
  737. throw GMException( EC_OVER_SIZE_ESTIMATE,
  738. "Clique tree size violates estimated size limit" );
  739. }
  740. // Create the topology-- all the trees in the forest
  741. clqsetWork.CreateTopology();
  742. // Nuke the moral graph
  743. DestroyDirectedGraph();
  744. // Bind the known distributions to their target nodes;
  745. _model.BindDistributions();
  746. // Reset/initialize the "lazy" switches
  747. SetReset();
  748. // Create the marginals in the cliques and sepsets
  749. CreateMarginals();
  750. _eState = BUILT;
  751. // Load and initialize the tree
  752. Reload();
  753. // Release the distributions from their target nodes
  754. _model.ClearDistributions();
  755. }
  756. DEFINEVP(GELEMLNK);
  757. //
  758. // Destroy the junction tree. Allow the GOBJMBN_CLIQSET object to be reused
  759. // for another cliquing operation later.
  760. //
  761. void GOBJMBN_CLIQSET :: Destroy ()
  762. {
  763. if ( ! Model().Pgraph() )
  764. return;
  765. int cCliques = 0;
  766. int cCliqueMemberArcs = 0;
  767. int cSepsetArcs = 0;
  768. int cUndirArcs = 0;
  769. int cRootCliqueArcs = 0;
  770. VPGELEMLNK vpgelm;
  771. GELEMLNK * pgelm;
  772. MODEL::MODELENUM mdlenum( Model() );
  773. while ( pgelm = mdlenum.PlnkelNext() )
  774. {
  775. bool bDelete = false;
  776. int eType = pgelm->EType();
  777. if ( pgelm->BIsEType( GELEM::EGELM_EDGE ) )
  778. {
  779. GEDGEMBN * pgedge;
  780. DynCastThrow( pgelm , pgedge );
  781. int eType = pgedge->EType();
  782. switch ( eType )
  783. {
  784. case GEDGEMBN::ETPROB:
  785. break;
  786. case GEDGEMBN::ETCLIQUE:
  787. // Clique membership arcs will go away automatically because
  788. // cliques will be deleted.
  789. ++cCliqueMemberArcs;
  790. break;
  791. case GEDGEMBN::ETJTREE:
  792. // Junction tree arcs will go away automatically because
  793. // cliques will be deleted.
  794. ++cSepsetArcs;
  795. break;
  796. case GEDGEMBN::ETUNDIR:
  797. // Undirected arcs must be deleted explicitly
  798. bDelete = true;
  799. ++cUndirArcs;
  800. break;
  801. case GEDGEMBN::ETCLIQSET:
  802. ++cRootCliqueArcs;
  803. break;
  804. default:
  805. THROW_ASSERT( EC_INTERNAL_ERROR, " GOBJMBN_CLIQSET::Destroy: Unrecognized edge object in graph" );
  806. break;
  807. }
  808. }
  809. else
  810. if ( pgelm->BIsEType( GELEM::EGELM_NODE ) )
  811. {
  812. GOBJMBN * pgobj;
  813. DynCastThrow( pgelm , pgobj );
  814. switch ( eType )
  815. {
  816. case GOBJMBN::EBNO_CLIQUE:
  817. {
  818. ++cCliques;
  819. bDelete = true;
  820. break;
  821. }
  822. case GOBJMBN::EBNO_CLIQUE_SET:
  823. case GOBJMBN::EBNO_NODE:
  824. case GOBJMBN::EBNO_PROP_TYPE:
  825. case GOBJMBN::EBNO_USER:
  826. break;
  827. default:
  828. THROW_ASSERT( EC_INTERNAL_ERROR, " GOBJMBN_CLIQSET::Destroy: Unrecognized node object in graph" );
  829. break;
  830. }
  831. }
  832. else
  833. {
  834. THROW_ASSERT( EC_INTERNAL_ERROR, " GOBJMBN_CLIQSET::Destroy: Unrecognized object in graph" );
  835. }
  836. if ( bDelete )
  837. vpgelm.push_back( pgelm );
  838. }
  839. assert(
  840. cCliques == _cCliques
  841. && cCliqueMemberArcs == _cCliqueMemberArcs
  842. && cSepsetArcs == _cSepsetArcs
  843. && cUndirArcs == _cUndirArcs
  844. );
  845. for ( int i = 0; i < vpgelm.size(); )
  846. {
  847. delete vpgelm[i++];
  848. }
  849. Clear();
  850. }
  851. void GOBJMBN_CLIQSET :: DestroyDirectedGraph ()
  852. {
  853. int cUndirArcs = 0;
  854. VPGELEMLNK vpgelm;
  855. GELEMLNK * pgelm;
  856. MODEL::MODELENUM mdlenum( Model() );
  857. while ( pgelm = mdlenum.PlnkelNext() )
  858. {
  859. if ( pgelm->BIsEType( GELEM::EGELM_EDGE ) )
  860. {
  861. GEDGEMBN * pgedge;
  862. DynCastThrow( pgelm , pgedge );
  863. int eType = pgedge->EType();
  864. switch ( eType )
  865. {
  866. case GEDGEMBN::ETUNDIR:
  867. vpgelm.push_back( pgelm );
  868. ++cUndirArcs;
  869. break;
  870. default:
  871. break;
  872. }
  873. }
  874. }
  875. assert( cUndirArcs == _cUndirArcs );
  876. _cUndirArcs = 0;
  877. for ( int i = 0; i < vpgelm.size(); )
  878. {
  879. delete vpgelm[i++];
  880. }
  881. }
  882. // Create and initialize all marginals tables
  883. void GOBJMBN_CLIQSET :: CreateMarginals ()
  884. {
  885. assert( _eState == CLIQUED ) ;
  886. //MSRDEVBUG: The class name qualifier should not be necessary here and below.
  887. WalkTree( true, & GOBJMBN_CLIQSET::BCreateClique, & GOBJMBN_CLIQSET::BCreateSepset );
  888. }
  889. // Reset the entire tree by reloading all marginals tables
  890. void GOBJMBN_CLIQSET :: LoadMarginals ()
  891. {
  892. assert( _eState == BUILT ) ;
  893. WalkTree( true, & GOBJMBN_CLIQSET::BLoadClique, & GOBJMBN_CLIQSET::BLoadSepset );
  894. _cqsetStat._cReload++;
  895. }
  896. // Apply the given member function(s) to every clique tree in the forest.
  897. int GOBJMBN_CLIQSET :: WalkTree (
  898. bool bDepthFirst, // Depth first or breadth first?
  899. PFNC_JTREE pfJtree, // Function to apply to each clique
  900. PFNC_SEPSET pfSepset ) // Function to apply to each sepset
  901. {
  902. int cClique = 0; // Don't count the clique set object
  903. int cWalk = 0; // Return count of cliques visited
  904. GNODENUM<GOBJMBN> benumChildren(false);
  905. benumChildren.SetETypeFollow( GEDGEMBN::ETCLIQSET );
  906. for ( benumChildren.Set( this );
  907. benumChildren.PnodeCurrent();
  908. benumChildren++ )
  909. {
  910. GOBJMBN * pgobj = *benumChildren;
  911. assert( pgobj->EType() == GNODEMBN::EBNO_CLIQUE );
  912. GOBJMBN_CLIQUE * pCliqueTreeRoot;
  913. DynCastThrow( pgobj, pCliqueTreeRoot );
  914. cWalk = bDepthFirst
  915. ? WalkDepthFirst( pCliqueTreeRoot, pfJtree, pfSepset )
  916. : WalkBreadthFirst( pCliqueTreeRoot, pfJtree, pfSepset );
  917. if ( cWalk < 0 )
  918. return -1;
  919. cClique += cWalk;
  920. }
  921. assert( cClique < 0 || cClique == _cCliques );
  922. return cClique;
  923. }
  924. //
  925. // Recursive depth-first walk down the tree.
  926. //
  927. // Apply the given member function(s), depth first from this clique.
  928. // If application function call returns false, walk is aborted and
  929. // -1 is returned; otherwise, count of cliques traversed is returned.
  930. int GOBJMBN_CLIQSET :: WalkDepthFirst (
  931. GOBJMBN_CLIQUE * pClique, // Starting point
  932. PFNC_JTREE pfJtree, // Function to apply to each clique
  933. PFNC_SEPSET pfSepset ) // Function to apply to each sepset
  934. {
  935. assert( pClique ) ;
  936. assert( pClique->IInferEngID() == IInferEngID() ) ;
  937. if ( pfJtree )
  938. {
  939. // Call the application function on the way down
  940. if ( ! (self.*pfJtree)( *pClique, true ) )
  941. return -1;
  942. }
  943. int cWalks = 1; // Count the clique we just processed above
  944. int cWalk = 0; // Return count of cliques visited
  945. GNODENUM<GOBJMBN_CLIQUE> benumChildren(false);
  946. benumChildren.SetETypeFollow( GEDGEMBN::ETJTREE );
  947. for ( benumChildren.Set( pClique );
  948. benumChildren.PnodeCurrent();
  949. benumChildren++ )
  950. {
  951. GOBJMBN_CLIQUE * pCliqueChild = NULL;
  952. GEDGEMBN_SEPSET * pgedge = NULL;
  953. if ( pfSepset )
  954. {
  955. // Call the application function on the way down
  956. DynCastThrow( benumChildren.PgedgeCurrent(), pgedge );
  957. if ( ! (self.*pfSepset)( *pgedge, true ) )
  958. return -1;
  959. }
  960. DynCastThrow( benumChildren.PnodeCurrent(), pCliqueChild );
  961. cWalk = WalkDepthFirst( pCliqueChild, pfJtree, pfSepset );
  962. if ( cWalk < 0 )
  963. return -1;
  964. cWalks += cWalk;
  965. if ( pfSepset )
  966. {
  967. assert( pgedge );
  968. // Call the application function on the way up
  969. if ( ! (self.*pfSepset)( *pgedge, false ) )
  970. return -1;
  971. }
  972. }
  973. if ( pfJtree )
  974. {
  975. // Call the application function on the way up
  976. if ( ! (self.*pfJtree)( *pClique, false ) )
  977. return -1;
  978. }
  979. return cWalks;
  980. }
  981. //
  982. // Non-recursive breadth-first walk down the tree.
  983. // No "up" actions are called using the function pointers.
  984. //
  985. int GOBJMBN_CLIQSET :: WalkBreadthFirst (
  986. GOBJMBN_CLIQUE * pClique, // Starting point
  987. PFNC_JTREE pfJtree, // Function to apply to each clique
  988. PFNC_SEPSET pfSepset ) // Function to apply to each sepset
  989. {
  990. assert( pClique ) ;
  991. assert( pClique->IInferEngID() == IInferEngID() ) ;
  992. VPGEDGEMBN_SEPSET vpgedgeThis;
  993. VPGEDGEMBN_SEPSET vpgedgeNext;
  994. VPGEDGEMBN_SEPSET * pvpgedgeThis = & vpgedgeThis;
  995. VPGEDGEMBN_SEPSET * pvpgedgeNext = & vpgedgeNext;
  996. VPGEDGEMBN_SEPSET * pvpgedgeTemp = NULL;
  997. GOBJMBN_CLIQUE * pgobjClique = NULL;
  998. GEDGEMBN_SEPSET * pgedgeSepset = NULL;
  999. // Count the cliques we process, including this one
  1000. int cWalk = 1;
  1001. // Starting clique is a special case; process it now
  1002. if ( pfJtree )
  1003. {
  1004. // Call the application function on the way down
  1005. if ( ! (self.*pfJtree)( *pClique, true ) )
  1006. return -1;
  1007. }
  1008. // Prepare an enumerator for child cliques
  1009. GNODENUM<GOBJMBN_CLIQUE> benumChildren(false);
  1010. benumChildren.SetETypeFollow( GEDGEMBN::ETJTREE );
  1011. // Since we don't have the edge that led us here, put a NULL
  1012. // in its place to start iteration
  1013. pvpgedgeNext->push_back(NULL);
  1014. // While there were entries at the last topological level...
  1015. while ( pvpgedgeNext->size() )
  1016. {
  1017. // Swap the array pointers and clear next pass array
  1018. pexchange( pvpgedgeThis, pvpgedgeNext );
  1019. pvpgedgeNext->clear();
  1020. for ( int iEdge = 0; iEdge < pvpgedgeThis->size(); iEdge++ )
  1021. {
  1022. pgedgeSepset = (*pvpgedgeThis)[iEdge];
  1023. pgobjClique = pgedgeSepset == NULL
  1024. ? pClique // This is the start of iteration
  1025. : pgedgeSepset->PclqChild();
  1026. assert( pgobjClique );
  1027. // Accumulate all child cliques of this clique,
  1028. // processing as necessary
  1029. for ( benumChildren.Set( pgobjClique );
  1030. benumChildren.PnodeCurrent();
  1031. benumChildren++ )
  1032. {
  1033. GEDGEMBN_SEPSET * pgedge;
  1034. DynCastThrow( benumChildren.PgedgeCurrent(), pgedge );
  1035. if ( pfSepset )
  1036. {
  1037. // Call the sepset application function on the way down
  1038. if ( ! (self.*pfSepset)( *pgedge, true ) )
  1039. return -1;
  1040. }
  1041. if ( pfJtree )
  1042. {
  1043. // Call the clique application function on the way down
  1044. GOBJMBN_CLIQUE * pCliqueChild = pgedge->PclqChild();
  1045. if ( ! (self.*pfJtree)( *pCliqueChild, true ) )
  1046. return -1;
  1047. }
  1048. cWalk++;
  1049. pvpgedgeNext->push_back( pgedge );
  1050. }
  1051. }
  1052. }
  1053. return cWalk;
  1054. }
  1055. //
  1056. // Terminology: "Create", "Init" and "Load":
  1057. //
  1058. // 'Create' means to size the dynamic arrays;
  1059. // 'Init' means to initialize them to 1.0;
  1060. // 'Load' means to multiply in the probabilities of the clique members.
  1061. //
  1062. bool GOBJMBN_CLIQSET :: BCreateClique ( GOBJMBN_CLIQUE & clique, bool bDownwards )
  1063. {
  1064. if ( ! bDownwards )
  1065. return true;
  1066. clique.CreateMarginals();
  1067. return true;
  1068. }
  1069. bool GOBJMBN_CLIQSET :: BLoadClique ( GOBJMBN_CLIQUE & clique, bool bDownwards )
  1070. {
  1071. if ( ! bDownwards )
  1072. return true;
  1073. clique.InitMarginals();
  1074. clique.LoadMarginals();
  1075. return true;
  1076. }
  1077. bool GOBJMBN_CLIQSET :: BCreateSepset ( GEDGEMBN_SEPSET & sepset, bool bDownwards )
  1078. {
  1079. if ( ! bDownwards )
  1080. return true;
  1081. sepset.CreateMarginals();
  1082. return true;
  1083. }
  1084. bool GOBJMBN_CLIQSET :: BLoadSepset ( GEDGEMBN_SEPSET & sepset, bool bDownwards )
  1085. {
  1086. if ( ! bDownwards )
  1087. return true;
  1088. sepset.InitMarginals();
  1089. return true;
  1090. }
  1091. // Return the "family" or "self" clique for a node
  1092. GOBJMBN_CLIQUE * GOBJMBN_CLIQSET :: PCliqueFromNode (
  1093. GNODEMBN * pgnd, // Node to find clique for
  1094. bool bFamily, // "family" clique if true, "self" clique if false
  1095. GEDGEMBN_CLIQ * * ppgedgeClique ) // return pointer to edge if not NULL
  1096. {
  1097. GEDGEMBN_CLIQ::FCQLROLE fcqlRole = bFamily
  1098. ? GEDGEMBN_CLIQ::FAMILY
  1099. : GEDGEMBN_CLIQ::SELF;
  1100. // Prepare to iterate over the source arcs
  1101. GNODENUM<GOBJMBN> benumMembers(true);
  1102. benumMembers.SetETypeFollow( GEDGEMBN::ETCLIQUE );
  1103. for ( benumMembers.Set( pgnd );
  1104. benumMembers.PnodeCurrent();
  1105. benumMembers++ )
  1106. {
  1107. GEDGEMBN_CLIQ * pgedgeClique;
  1108. DynCastThrow( benumMembers.PgedgeCurrent(), pgedgeClique );
  1109. GOBJMBN_CLIQUE * pgobjClique = pgedgeClique->PclqParent();
  1110. if ( pgobjClique->IInferEngID() != IInferEngID() )
  1111. continue; // not an edge for this junction tree
  1112. if ( pgedgeClique->IFcqlRole() & fcqlRole )
  1113. {
  1114. if ( ppgedgeClique )
  1115. *ppgedgeClique = pgedgeClique;
  1116. return pgedgeClique->PclqParent();
  1117. }
  1118. }
  1119. assert( false );
  1120. return NULL;
  1121. }
  1122. //
  1123. // Enter evidence for a node.
  1124. //
  1125. void GOBJMBN_CLIQSET :: EnterEvidence ( GNODEMBN * pgnd, const CLAMP & clamp )
  1126. {
  1127. // Get the pointer to the node's "self" clique and the edge leading to it
  1128. GEDGEMBN_CLIQ * pgedgeClique = NULL;
  1129. GOBJMBN_CLIQUE * pCliqueSelf = PCliqueFromNode( pgnd, false, & pgedgeClique );
  1130. ASSERT_THROW( pCliqueSelf,
  1131. EC_INTERNAL_ERROR,
  1132. "GOBJMBN_CLIQSET::EnterEvidence: can\'t find self clique" );
  1133. assert( pgedgeClique );
  1134. // Update with evidence if it has changed
  1135. if ( pgedgeClique->Clamp() != clamp )
  1136. {
  1137. // Evidence is NOT the same as the old evidence
  1138. pgedgeClique->Clamp() = clamp;
  1139. // Indicate that we must reload the tree
  1140. SetReset();
  1141. pCliqueSelf->SetCollect();
  1142. _cqsetStat._cEnterEv++;
  1143. }
  1144. }
  1145. //
  1146. // Return the evidence "clamp" for a node. It is stored in the edge
  1147. // between the node and its "self" clique: the highest clique in the tree
  1148. // of which the node is a member.
  1149. //
  1150. void GOBJMBN_CLIQSET :: GetEvidence ( GNODEMBN * pgnd, CLAMP & clamp )
  1151. {
  1152. // Get the pointer to the node's "self" clique and the edge leading to it
  1153. GEDGEMBN_CLIQ * pgedgeClique = NULL;
  1154. GOBJMBN_CLIQUE * pCliqueSelf = PCliqueFromNode( pgnd, false, & pgedgeClique );
  1155. ASSERT_THROW( pCliqueSelf,
  1156. EC_INTERNAL_ERROR,
  1157. "GOBJMBN_CLIQSET::GetEvidence: can\'t find self clique" );
  1158. assert( pgedgeClique );
  1159. clamp = pgedgeClique->Clamp();
  1160. }
  1161. void GOBJMBN_CLIQSET :: GetBelief ( GNODEMBN * pgnd, MDVCPD & mdvBel )
  1162. {
  1163. GEDGEMBN_CLIQ * pgedgeClique = NULL;
  1164. GOBJMBN_CLIQUE * pCliqueFamily = PCliqueFromNode( pgnd, true, & pgedgeClique );
  1165. ASSERT_THROW( pCliqueFamily,
  1166. EC_INTERNAL_ERROR,
  1167. "GOBJMBN_CLIQSET::GetBelief: can\'t find family clique" );
  1168. // Perform inference if necessary
  1169. Infer();
  1170. // Marginalize the clique down to one node
  1171. GNODEMBND * pgndd;
  1172. DynCastThrow( pgnd, pgndd );
  1173. pgedgeClique->MiterNodeBelief().MarginalizeBelief( mdvBel, pgndd );
  1174. _cqsetStat._cGetBel++;
  1175. }
  1176. PROB GOBJMBN_CLIQSET :: ProbNorm ()
  1177. {
  1178. // MSRDEVBUG
  1179. /*
  1180. Reset();
  1181. CollectEvidence();
  1182. */
  1183. Infer();
  1184. _cqsetStat._cProbNorm++;
  1185. return _probNorm;
  1186. }
  1187. //
  1188. // Reload all marginals, reset the trees
  1189. //
  1190. void GOBJMBN_CLIQSET :: Reload ()
  1191. {
  1192. SetReset( true );
  1193. Reset();
  1194. }
  1195. //
  1196. // Reset all marginals, restore all clamped evidence and
  1197. // perform the initial inference pass.
  1198. //
  1199. void GOBJMBN_CLIQSET :: Reset ()
  1200. {
  1201. assert( EState() >= BUILT );
  1202. if ( ! _bReset )
  1203. return;
  1204. _probNorm = 1.0;
  1205. LoadMarginals();
  1206. SetReset( false );
  1207. // Initialize the entire tree for inference
  1208. #ifdef INFERINIT
  1209. InferInit();
  1210. #endif
  1211. SetCollect(true);
  1212. }
  1213. // Perform an inference cycle if necessary
  1214. void GOBJMBN_CLIQSET :: Infer ()
  1215. {
  1216. Reset(); // Reloads the tree if necessary
  1217. if ( ! BCollect() )
  1218. return;
  1219. #ifdef DUMPCLIQUESET
  1220. cout << "\n\n===============================================================";
  1221. cout << "\n============= Dump of clique tree before inference ===============\n";
  1222. Dump();
  1223. cout << "\n========= End Dump of clique tree before inference ===============";
  1224. cout << "\n===============================================================\n\n";
  1225. cout << "\n\nGOBJMBN_CLIQSET::Infer: begin.";
  1226. #endif
  1227. CollectEvidence();
  1228. DistributeEvidence();
  1229. #ifdef CONSISTENCY
  1230. CheckConsistency();
  1231. #endif
  1232. SetCollect( false );
  1233. #ifdef DUMPCLIQUESET
  1234. cout << "\n\n===============================================================";
  1235. cout << "\n============= Dump of clique tree after inference ===============\n";
  1236. Dump();
  1237. cout << "\n========= End Dump of clique tree after inference ===============";
  1238. cout << "\n===============================================================\n\n";
  1239. cout << "\nGOBJMBN_CLIQSET::Infer: end.\n\n";
  1240. #endif
  1241. }
  1242. // Perform initial inference collect/distribute cycle
  1243. void GOBJMBN_CLIQSET :: InferInit ()
  1244. {
  1245. #ifdef DUMPCLIQUESET
  1246. cout << "\n\n===============================================================";
  1247. cout << "\n============= Dump of clique tree before inference INIT ======\n";
  1248. Dump();
  1249. cout << "\n========= End Dump of clique tree before inference INIT ======";
  1250. cout << "\n===============================================================\n\n";
  1251. cout << "\n\nGOBJMBN_CLIQSET::InferInit: begin.";
  1252. #endif
  1253. CollectEvidenceInit();
  1254. DistributeEvidenceInit();
  1255. #ifdef DUMPCLIQUESET
  1256. cout << "\n\n===============================================================";
  1257. cout << "\n============= Dump of clique tree after inference INIT =======\n";
  1258. Dump();
  1259. cout << "\n========= End Dump of clique tree after inference INIT ========";
  1260. cout << "\n================================================================\n\n";
  1261. cout << "\nGOBJMBN_CLIQSET::InferInit: end.\n\n";
  1262. #endif
  1263. }
  1264. void GOBJMBN_CLIQSET :: CollectEvidence()
  1265. {
  1266. WalkTree( true, BCollectEvidenceAtRoot,
  1267. BCollectEvidenceAtSepset );
  1268. _cqsetStat._cCollect++;
  1269. }
  1270. void GOBJMBN_CLIQSET :: DistributeEvidence()
  1271. {
  1272. WalkTree( true, BDistributeEvidenceAtRoot,
  1273. BDistributeEvidenceAtSepset );
  1274. }
  1275. void GOBJMBN_CLIQSET :: CollectEvidenceInit ()
  1276. {
  1277. WalkTree( true, BCollectInitEvidenceAtRoot,
  1278. BCollectInitEvidenceAtSepset );
  1279. }
  1280. void GOBJMBN_CLIQSET :: DistributeEvidenceInit ()
  1281. {
  1282. WalkTree( true, BDistributeInitEvidenceAtRoot,
  1283. BDistributeInitEvidenceAtSepset );
  1284. }
  1285. void GOBJMBN_CLIQSET :: CheckConsistency ()
  1286. {
  1287. WalkTree( true, NULL, BConsistentSepset );
  1288. }
  1289. bool GOBJMBN_CLIQSET :: BConsistentSepset ( GEDGEMBN_SEPSET & sepset, bool bDownwards )
  1290. {
  1291. if ( ! bDownwards )
  1292. return true;
  1293. return sepset.BConsistent();
  1294. }
  1295. // When the collection cycle has completed for a tree, recompute the
  1296. // "prob norm" value.
  1297. bool GOBJMBN_CLIQSET :: BCollectEvidenceAtRoot ( GOBJMBN_CLIQUE & clique, bool bDownwards )
  1298. {
  1299. if ( bDownwards || ! clique.BRoot() )
  1300. return true;
  1301. // This is a root clique at the end of the collection cycle.
  1302. // Normalize the clique and maintain the norm of the the probability
  1303. // of the tree.
  1304. // MSRDEVBUG: (Explain this better!)
  1305. REAL rProb = clique.Marginals().RSum();
  1306. _probNorm *= rProb;
  1307. if ( rProb != 0.0 )
  1308. {
  1309. rProb = 1.0 / rProb;
  1310. clique.Marginals().Multiply( rProb );
  1311. }
  1312. #ifdef DUMPCLIQUESET
  1313. cout << "\nCollect Evidence (root), clique "
  1314. << clique._iClique
  1315. << ", root = "
  1316. << int(clique._bRoot)
  1317. << ", prob norm = "
  1318. << _probNorm;
  1319. #endif
  1320. return true;
  1321. }
  1322. bool GOBJMBN_CLIQSET :: BDistributeEvidenceAtRoot ( GOBJMBN_CLIQUE & clique, bool bDownwards )
  1323. {
  1324. if ( ! bDownwards || ! clique.BRoot() )
  1325. return true;
  1326. #ifdef DUMPCLIQUESET
  1327. cout << "\nDistribute Evidence (root), clique "
  1328. << clique._iClique
  1329. << ", root = "
  1330. << int(clique._bRoot);
  1331. #endif
  1332. return true;
  1333. }
  1334. bool GOBJMBN_CLIQSET :: BCollectEvidenceAtSepset ( GEDGEMBN_SEPSET & sepset, bool bDownwards )
  1335. {
  1336. GOBJMBN_CLIQUE * pCliqueChild = sepset.PclqChild();
  1337. GOBJMBN_CLIQUE * pCliqueParent = sepset.PclqParent();
  1338. if ( bDownwards )
  1339. return true;
  1340. #ifdef DUMPCLIQUESET
  1341. cout << "\nCollect Evidence (sepset), clique "
  1342. << pCliqueChild->_iClique
  1343. << ", root = "
  1344. << int(pCliqueChild->_bRoot)
  1345. << ", parent = "
  1346. << pCliqueParent->_iClique
  1347. ;
  1348. cout.flush();
  1349. #endif
  1350. if ( ! pCliqueChild->BCollect() )
  1351. return true;
  1352. pCliqueParent->SetCollect();
  1353. sepset.UpdateParentClique();
  1354. SetCollect( false );
  1355. return true;
  1356. }
  1357. bool GOBJMBN_CLIQSET :: BDistributeEvidenceAtSepset ( GEDGEMBN_SEPSET & sepset, bool bDownwards )
  1358. {
  1359. if ( ! bDownwards )
  1360. return true;
  1361. #ifdef DUMPCLIQUESET
  1362. GOBJMBN_CLIQUE * pCliqueChild = sepset.PclqChild();
  1363. GOBJMBN_CLIQUE * pCliqueParent = sepset.PclqParent();
  1364. cout << "\nDistribute Evidence (sepset), clique "
  1365. << pCliqueParent->_iClique
  1366. << ", root = "
  1367. << int(pCliqueParent->_bRoot)
  1368. << ", child = "
  1369. << pCliqueChild->_iClique
  1370. ;
  1371. cout.flush();
  1372. #endif
  1373. sepset.UpdateChildClique();
  1374. return true;
  1375. }
  1376. bool GOBJMBN_CLIQSET :: BCollectInitEvidenceAtSepset ( GEDGEMBN_SEPSET & sepset, bool bDownwards )
  1377. {
  1378. if ( bDownwards )
  1379. return true;
  1380. #ifdef DUMPCLIQUESET
  1381. GOBJMBN_CLIQUE * pCliqueChild = sepset.PclqChild();
  1382. GOBJMBN_CLIQUE * pCliqueParent = sepset.PclqParent();
  1383. cout << "\nCollect Initial Evidence (sepset), clique "
  1384. << pCliqueChild->_iClique
  1385. << ", root = "
  1386. << int(pCliqueChild->_bRoot)
  1387. << ", parent = "
  1388. << pCliqueParent->_iClique
  1389. ;
  1390. cout.flush();
  1391. #endif
  1392. sepset.BalanceCliquesCollect();
  1393. return true;
  1394. }
  1395. bool GOBJMBN_CLIQSET :: BDistributeInitEvidenceAtSepset ( GEDGEMBN_SEPSET & sepset, bool bDownwards )
  1396. {
  1397. if ( ! bDownwards )
  1398. return true;
  1399. #ifdef DUMPCLIQUESET
  1400. GOBJMBN_CLIQUE * pCliqueParent = sepset.PclqParent();
  1401. GOBJMBN_CLIQUE * pCliqueChild = sepset.PclqChild();
  1402. cout << "\nDistribute Initial Evidence (sepset), clique "
  1403. << pCliqueParent->_iClique
  1404. << ", root = "
  1405. << int(pCliqueParent->_bRoot)
  1406. << ", child = "
  1407. << pCliqueChild->_iClique
  1408. ;
  1409. cout.flush();
  1410. #endif
  1411. sepset.BalanceCliquesDistribute();
  1412. return true;
  1413. }
  1414. bool GOBJMBN_CLIQSET :: BCollectInitEvidenceAtRoot ( GOBJMBN_CLIQUE & clique, bool bDownwards )
  1415. {
  1416. if ( bDownwards || ! clique.BRoot() )
  1417. return true;
  1418. #ifdef DUMPCLIQUESET
  1419. cout << "\nCollect Initial Evidence at root, clique "
  1420. << clique._iClique
  1421. << ", root = "
  1422. << int(clique._bRoot);
  1423. #endif
  1424. clique.Marginals().Normalize();
  1425. return true;
  1426. }
  1427. bool GOBJMBN_CLIQSET :: BDistributeInitEvidenceAtRoot ( GOBJMBN_CLIQUE & clique, bool bDownwards )
  1428. {
  1429. return true;
  1430. }
  1431. void GOBJMBN_CLIQSET :: Dump ()
  1432. {
  1433. WalkTree( true, BDumpClique, BDumpSepset );
  1434. MARGSUBITER::Dump();
  1435. }
  1436. bool GOBJMBN_CLIQSET :: BDumpSepset ( GEDGEMBN_SEPSET & sepset, bool bDownwards )
  1437. {
  1438. if ( ! bDownwards )
  1439. return true;
  1440. sepset.Dump();
  1441. return true;
  1442. }
  1443. bool GOBJMBN_CLIQSET :: BDumpClique ( GOBJMBN_CLIQUE & clique, bool bDownwards )
  1444. {
  1445. if ( ! bDownwards )
  1446. return true;
  1447. clique.Dump();
  1448. return true;
  1449. }
  1450. // End of CLIQUE.CPP