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.

440 lines
13 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1997 - 1998
  6. //
  7. // File: expand.cpp
  8. //
  9. //--------------------------------------------------------------------------
  10. //
  11. // expand.cpp: CI expansion
  12. //
  13. #include <basetsd.h>
  14. #include "basics.h"
  15. #include "algos.h"
  16. #include "expand.h"
  17. /*
  18. The Causal Independence model expansion.
  19. In all cases, the zeroth state is considered the "normal" state; all
  20. other states are "abnormal" in some sense.
  21. For each CI node, new "expansion" nodes and arcs are created. All
  22. generated nodes have the same state space as the original CI node.
  23. 1) For each parent, a new intermediate node is created.
  24. 2) A "leak" node is created for the CI node, and one for each
  25. of the parents except the last.
  26. 3) The nodes are linked in a chain, such as:
  27. (A) (B) (A) (B)
  28. | | becomes | |
  29. \ / (Pca) (Pcb)
  30. \ / | |
  31. (C) (Lc) -- (La) -- (C')
  32. 4) In other words, the intermediate nodes are between the original
  33. parents and the CI leak nodes or the final, modified C node (labeled
  34. C').
  35. 5) Probabilities for C given abnormal states of each parent are moved
  36. to the intermediate parent nodes (labeled Pca and Pcb above).
  37. 6) Probabilities of the primary leak node (Lc) are set to the "leak"
  38. probabilties of the original node; namely, the pvector representing
  39. all parents in a normal state (0,0,...).
  40. 7) The replacement node for C (labeled C') is just another "leak" node
  41. for the original node given its final parent. (This is a topological
  42. optimization.)
  43. 8) All of the leak nodes are deterministic; i.e., their ptables
  44. contain only 0 or 1.0 in every entry.
  45. Topological consistency is maintained as follows:
  46. 1) All newly generated nodes and arcs are marked with the "Expansion"
  47. bit flag.
  48. 2) The target node is marked as "Expanded". Its distribution reference
  49. is replaced with a reference to a distribution created to represent
  50. the expanded distribution.
  51. 3) New nodes are added for leak and expansion parents; they are marked
  52. accordingly as "Expansion" and "Leak".
  53. 3) New arcs are added between "Expanded" (modified) nodes and their new
  54. expansion parents, as well as between expansion nodes. These are marked
  55. as "Expansion" arcs.
  56. Note that the ordering of the parents of a node cannot change as a result of CI expansion.
  57. During cliquing and inference, if a node is marked as "Expanded", only its "Expansion"
  58. arcs are considered true parents.
  59. During expansion tear-down (in Destroy()), all "Expansion" by-products are deleted.
  60. "Expanded" flags are cleared from all remaining nodes and arcs. This must be a
  61. complete "undo" of all that expansion accomplished. Note that generated distributions
  62. (which are not recorded in the model's distribution map) will be automatically
  63. deleted
  64. */
  65. GOBJMBN_MBNET_EXPANDER :: GOBJMBN_MBNET_EXPANDER ( MBNET & model )
  66. : MBNET_MODIFIER(model),
  67. _propmgr(model),
  68. _cNodesExpanded(0),
  69. _cNodesCreated(0),
  70. _cArcsCreated(0)
  71. {
  72. }
  73. GOBJMBN_MBNET_EXPANDER :: ~ GOBJMBN_MBNET_EXPANDER ()
  74. {
  75. Destroy();
  76. }
  77. // Return true if no modidfications were performed.
  78. bool GOBJMBN_MBNET_EXPANDER :: BMoot ()
  79. {
  80. return _cNodesExpanded == 0;
  81. }
  82. // Perform any creation-time operations
  83. void GOBJMBN_MBNET_EXPANDER :: Create ()
  84. {
  85. // Test whether network has already been CI-expanded
  86. ASSERT_THROW( ! _model.BFlag( EIBF_Expanded ),
  87. EC_INTERNAL_ERROR,
  88. "network expansion called on expanded network" );
  89. // Create the topology if necessary
  90. _model.CreateTopology();
  91. // Connect the nodes to their distributions
  92. _model.BindDistributions();
  93. // Collect the expandable nodes
  94. GOBJMBN * pgmobj;
  95. VPGNODEMBND vpgndd;
  96. MBNET::ITER mbnit( _model, GOBJMBN::EBNO_NODE );
  97. for ( ; pgmobj = *mbnit ; ++mbnit)
  98. {
  99. ZSREF zsrName = mbnit.ZsrCurrent();
  100. GNODEMBN * pbnode;
  101. DynCastThrow( pgmobj, pbnode );
  102. assert( zsrName == pbnode->ZsrefName() );
  103. assert( ! pbnode->BFlag( EIBF_Expanded ) );
  104. assert( ! pbnode->BFlag( EIBF_Expansion ) );
  105. // For now, this routine only handles discrete nodes
  106. GNODEMBND * pbnoded;
  107. DynCastThrow( pbnode, pbnoded );
  108. // Does this node have any parents?
  109. // Is this a CI node?
  110. assert( pbnoded->BHasDist() );
  111. BNDIST::EDIST ed = pbnoded->Bndist().Edist() ;
  112. if ( ed <= BNDIST::ED_SPARSE )
  113. continue;
  114. ASSERT_THROW( ed == BNDIST::ED_CI_MAX,
  115. EC_NYI,
  116. "attempt to expand non-MAX CI node" );
  117. vpgndd.push_back( pbnoded );
  118. }
  119. // Expand them
  120. for ( int ind = 0; ind < vpgndd.size(); )
  121. {
  122. Expand( *vpgndd[ind++] );
  123. _cNodesExpanded++;
  124. }
  125. _model.BSetBFlag( EIBF_Expanded );
  126. }
  127. // Perform any special destruction
  128. void GOBJMBN_MBNET_EXPANDER :: Destroy ()
  129. {
  130. ASSERT_THROW( _model.BFlag( EIBF_Expanded ),
  131. EC_INTERNAL_ERROR,
  132. "network expansion undo called on unexpanded network" );
  133. int cNodesExpanded = 0;
  134. int cNodesCreated = 0;
  135. int cArcsCreated = 0;
  136. VPGNODEMBN vpgnd;
  137. GELEMLNK * pgelm;
  138. MODEL::MODELENUM mdlenum( Model() );
  139. while ( pgelm = mdlenum.PlnkelNext() )
  140. {
  141. // See if it's an expansion-generated edge
  142. if ( pgelm->BIsEType( GELEM::EGELM_EDGE ) )
  143. {
  144. GEDGEMBN * pgedge;
  145. DynCastThrow( pgelm , pgedge );
  146. if ( pgedge->EType() == GEDGEMBN::ETPROB )
  147. {
  148. GNODEMBN * pgndSource = dynamic_cast<GNODEMBN *> ( pgedge->PobjSource() );
  149. GNODEMBN * pgndSink = dynamic_cast<GNODEMBN *> ( pgedge->PobjSink() );
  150. if ( pgndSource && pgndSink )
  151. {
  152. // Count this edge if either end connects to an expansion by-product
  153. if ( pgndSource->BFlag( EIBF_Expansion ) || pgndSink->BFlag( EIBF_Expansion ) )
  154. {
  155. // This arc was created during expansion; it will be deleted along with
  156. // the expansion node(s) it connects to.
  157. cArcsCreated++;
  158. }
  159. }
  160. }
  161. }
  162. else
  163. if ( pgelm->BIsEType( GELEM::EGELM_NODE ) )
  164. {
  165. GNODEMBND * pgndd = dynamic_cast<GNODEMBND *>(pgelm);
  166. if ( pgndd )
  167. {
  168. if ( pgndd->BFlag( EIBF_Expansion ) )
  169. {
  170. // Expansion node; kill it
  171. vpgnd.push_back( pgndd );
  172. cNodesCreated++;
  173. }
  174. else
  175. if ( pgndd->BFlag( EIBF_Expanded ) )
  176. {
  177. // Expanded node; zap the generated distribution, clear all flags
  178. pgndd->ClearDist();
  179. pgndd->BSetBFlag( EIBF_Expanded, false );
  180. cNodesExpanded++;
  181. }
  182. }
  183. }
  184. }
  185. assert( cNodesCreated == _cNodesCreated
  186. && cArcsCreated == _cArcsCreated
  187. && cNodesExpanded == _cNodesExpanded );
  188. for ( int i = 0; i < vpgnd.size(); )
  189. {
  190. _model.DeleteElem( vpgnd[i++] ) ;
  191. }
  192. // Disconnect the nodes from their distributions. Note that this destroys distributions
  193. // generated during expansion, since their reference counts will go to zero.
  194. _model.ClearDistributions();
  195. // Unmark the network
  196. _model.BSetBFlag( EIBF_Expanded, false );
  197. }
  198. //
  199. // Perform the expansion operation against a node.
  200. //
  201. // This creates:
  202. //
  203. // A parentless "leak" node for the ensemble, marked as "expansion"
  204. //
  205. // A "causal" node for each original parent, marked as "expansion"
  206. //
  207. // An "expand/leak" node for each original parent but the last. The given
  208. // node is (reversably) modified for reuse as the last in the chain. These
  209. // nodes are marked as "expanded" and "expansion", so that expansion
  210. // arcs will be considered real parents by GNODEMBN::GetParents().
  211. //
  212. void GOBJMBN_MBNET_EXPANDER :: Expand ( GNODEMBND & gndd )
  213. {
  214. // Guarantee that the node to be expanded has a sparse distribution
  215. assert( ! gndd.BFlag( EIBF_Expanded ) );
  216. assert( gndd.BHasDist() );
  217. assert( gndd.Bndist().BSparse() );
  218. // Get the array of parents
  219. VPGNODEMBN vpgndParents;
  220. gndd.GetParents( vpgndParents );
  221. int cParent = vpgndParents.size();
  222. VIMD vimd1Dim(1); // Useful 1-dimensional subscript vector
  223. // Build a leak distribution to use either on the leak
  224. // node or on this node if the node has no parents.
  225. BNDIST * pbndistLeak = new BNDIST();
  226. {
  227. // Locate the leak vector
  228. const VLREAL * pvlrLeak = gndd.Bndist().PVlrLeak();
  229. ASSERT_THROW( pvlrLeak,
  230. EC_INTERNAL_ERROR,
  231. "node CI expansion cannot locate leak/default vector" );
  232. assert( pvlrLeak->size() == gndd.CState() );
  233. // Build a leak distribution
  234. assert( pvlrLeak->size() == gndd.CState() );
  235. vimd1Dim[0] = gndd.CState();
  236. pbndistLeak->SetDense( vimd1Dim );
  237. MDVCPD & mdvLeak = pbndistLeak->Mdvcpd();
  238. mdvLeak = *pvlrLeak;
  239. }
  240. if ( cParent == 0 )
  241. {
  242. // CI node has no parents; use leak distribution.
  243. gndd.SetDist( pbndistLeak );
  244. }
  245. // Use the special "internal symbol" character
  246. char chMark = _model.ChInternal();
  247. SZC szcNode = gndd.ZsrefName().Szc();
  248. // Start the CI expansion chain with a node representing the "leak" or
  249. // background event.
  250. ZSTR zsName;
  251. // Format the name "$Leak$Nodename"
  252. zsName.Format( "%cLeak%c%s", chMark, chMark, szcNode );
  253. // Create the node, initialize it and add it to the network
  254. GNODEMBND * pgnddLeak = new GNODEMBND;
  255. pgnddLeak->BSetBFlag( EIBF_Leak );
  256. pgnddLeak->BSetBFlag( EIBF_Expansion );
  257. pgnddLeak->SetStates( gndd.VzsrStates() );
  258. _model.AddElem( zsName, pgnddLeak );
  259. _cNodesCreated++;
  260. pgnddLeak->SetDist( pbndistLeak );
  261. // Prepare to iterate over the parents
  262. // Dense dimensioned subscript vector for causal parent
  263. VIMD vimdCausal(2);
  264. // Dense dimensioned subscript vector for leak/expansion parent
  265. VIMD vimdLeak(3);
  266. // Sparse dimensioned subscript vector for real parent
  267. VIMD vimdTarget( gndd.Bndist().VimdDim().size() - 1 );
  268. // Set up a "normal" vector for causal parents
  269. VLREAL vlrNormal( gndd.CState() );
  270. vlrNormal = 0.0;
  271. vlrNormal[0] = 1.0;
  272. // Sparse map for this node's distribution. Note that the last cycle through
  273. // the loop will replace the distribution on this node. However, the
  274. // reference object will increment the reference count on the
  275. // distribution object, and all created distributions will disappear when
  276. // the expansion is reversed.
  277. REFBNDIST refbndThis = gndd.RefBndist();
  278. const MPCPDD & dmap = refbndThis->Mpcpdd();
  279. for ( int iParent = 0; iParent < cParent; iParent++ )
  280. {
  281. // Set to create a new node if this isn't the last parent
  282. bool bNew = iParent+1 < cParent;
  283. GNODEMBND * pgnddParent;
  284. DynCastThrow( vpgndParents[iParent], pgnddParent );
  285. SZC szcParent = pgnddParent->ZsrefName().Szc();
  286. // Create a new leak node if this isn't last parent
  287. GNODEMBND * pgnddLeakNew = NULL;
  288. if ( bNew )
  289. {
  290. // Format the name "$Expand$Child$Parent"
  291. zsName.Format( "%cExpand%c%s%c%s",
  292. chMark, chMark, szcNode, chMark, szcParent );
  293. _model.AddElem( zsName, pgnddLeakNew = new GNODEMBND );
  294. _cNodesCreated++;
  295. pgnddLeakNew->SetStates( gndd.VzsrStates() );
  296. pgnddLeakNew->BSetBFlag( EIBF_Expansion );
  297. }
  298. else
  299. {
  300. pgnddLeakNew = & gndd;
  301. }
  302. pgnddLeakNew->BSetBFlag( EIBF_Expanded );
  303. // Create a "causal" node for each parent to contain the probabilities
  304. // for that parent's abnormal states.
  305. GNODEMBND * pgnddCausal = new GNODEMBND;
  306. pgnddCausal->BSetBFlag( EIBF_Expansion );
  307. pgnddCausal->SetStates( gndd.VzsrStates() );
  308. // Format the name "$Causal$Child$Parent"
  309. zsName.Format( "%cCausal%c%s%c%s",
  310. chMark, chMark, szcNode, chMark, szcParent );
  311. _model.AddElem( zsName, pgnddCausal );
  312. _cNodesCreated++;
  313. // Add the appropriate edges:
  314. // from the original parent to the causal parent
  315. _model.AddElem( new GEDGEMBN_PROB( pgnddParent, pgnddCausal) ) ;
  316. // from the old leak node to the new leak node
  317. _model.AddElem( new GEDGEMBN_PROB( pgnddLeak, pgnddLeakNew ) );
  318. // from the causal to the new "leak" node
  319. _model.AddElem( new GEDGEMBN_PROB( pgnddCausal, pgnddLeakNew ) );
  320. _cArcsCreated += 3;
  321. // Set the priors for the new "causal" pseudo-parent
  322. // p( causal | originalParent )
  323. {
  324. BNDIST * pbndist = new BNDIST;
  325. vimdCausal[0] = pgnddParent->CState();
  326. vimdCausal[1] = gndd.CState();
  327. pbndist->SetDense( vimdCausal );
  328. MDVCPD & mdvCausal = pbndist->Mdvcpd();
  329. vclear( vimdCausal, 0);
  330. vclear( vimdTarget, 0);
  331. vimd1Dim[0] = 0;
  332. // Zero vector is deterministic based on parent being in normal state
  333. mdvCausal.UpdatePartial( vimd1Dim, vlrNormal );
  334. for ( int iAbnorm = 0; ++iAbnorm < pgnddParent->CState(); )
  335. {
  336. // Look up priors in original node for each abnormal state of parent
  337. vimd1Dim[0] = iAbnorm;
  338. assert( iParent < vimdTarget.size() );
  339. vimdTarget[iParent] = iAbnorm;
  340. MPCPDD::const_iterator itdm = dmap.find(vimdTarget);
  341. ASSERT_THROW( itdm != dmap.end(), EC_MDVECT_MISUSE, "cannot locate abnormal parent probs" );
  342. mdvCausal.UpdatePartial( vimd1Dim, (*itdm).second );
  343. }
  344. // Bind the distribution to the causal node
  345. pgnddCausal->SetDist( pbndist );
  346. }
  347. // Set the priors for the new "leak" node
  348. // p( newLeakExpand | oldLeakExpand, causal )
  349. {
  350. BNDIST * pbndist = new BNDIST;
  351. int cValue = gndd.CState();
  352. assert( cValue == pgnddCausal->CState() && cValue == pgnddLeak->CState() );
  353. vclear( vimdLeak, cValue );
  354. pbndist->SetDense( vimdLeak );
  355. MDVCPD & mdvLeak = pbndist->Mdvcpd();
  356. for ( int il = 0; il < cValue; il++ )
  357. {
  358. vimdLeak[0] = il;
  359. for ( int ic = 0; ic < cValue; ic++ )
  360. {
  361. vimdLeak[1] = ic;
  362. for ( int iself = 0; iself < cValue; iself++ )
  363. {
  364. vimdLeak[2] = iself;
  365. int ivalue = il >= ic ? il : ic;
  366. assert( ivalue < cValue );
  367. REAL r = ivalue == iself ? 1.0 : 0.0;
  368. mdvLeak[vimdLeak] = r;
  369. }
  370. }
  371. }
  372. pgnddLeakNew->SetDist( pbndist );
  373. }
  374. // Verify that the dimensions of the created nodes match their
  375. // created dense distributions
  376. assert( pgnddCausal->BCheckDistDense() );
  377. assert( pgnddLeakNew->BCheckDistDense() );
  378. pgnddLeak = pgnddLeakNew;
  379. }
  380. }