Main Page   Class Hierarchy   Alphabetical List   Compound List   Compound Members  

Hugin 6.3 C++ API Documentation

Contents

Introduction

These pages are meant as a help for developers programming against the Hugin C++ API 6.3. Short descriptions can be found for all classes and their members. However, additional information might be relevant for different tasks. In such cases, the Hugin API 6.3 Reference Manual will be a good place to look. It contains detailed documentation of the Hugin C API 6.3 which is the basis of the Hugin C++ API 6.3. The Hugin API 6.3 Reference Manual can be downloaded from Hugin Expert A/S - Documentation.

The Hugin C++ API 6.3 contains a high performance inference engine that can be used as the core of knowledge based systems built using Bayesian belief networks or influence diagrams. A knowledge engineer can build knowledge bases that model the application domain, using probabilistic descriptions of causal relationships in the domain. Given this description, the Hugin inference engine can perform fast and accurate reasoning.

The Hugin C++ API 6.3 is organized as a C++ shared library. Classes and member methods are provided for tasks such as construction of networks, performing inference, etc. The Hugin C++ API 6.3 also provides an exception based mechanism for handling errors. Moreover, the Hugin C++ API 6.3 contains facilities for constructing and working with Object Oriented Bayesian Networks.

General Information

The Hugin C++ API 6.3 consists of one header file and two libraries, one for single-precision computations and one for double-precision computations.

The header file is named hugin on all platforms.

When using the double-precision version of the Hugin C++ API, you must specify the preprocessor symbol H_DOUBLE during compilation (this is usually done by giving the option -DH_DOUBLE to the compiler).

On Windows platforms, each library consists of two files: an import library and a DLL. For the single-precision library, the import library is named hugincpp.lib and the DLL is named hugincpp.dll. For the double-precision library, the files are named hugincpp2.lib and hugincpp2.dll, respectively. In the linking step, you must link against the appropriate import library. When running the executable program, the appropriate DLL file must be accessible in a directory mentioned by the PATH environment variable.

On Linux and Solaris platforms, the single-precision library is named libhugincpp.so (specify -lhugincpp when linking), and the double-precision library is named libhugincpp2.so. When running the executable program, the linker must be able to find the appropriate library file. This can be ensured by mentioning the directory where the library resides in the LD_LIBRARY_PATH environment variable.

Classes and Constants

The Hugin C++ API uses various classes for representing domains, nodes, tables, cliques, junction trees, exceptions, etc. A set of enumeration types is used to represent triangulation methods, node categories, etc. The class hierarchy can be seen in the class hierarchy

Error Handling

Several types of errors can occur when using a class or member method from the Hugin C++ API. These errors can be the result of errors in the application program, of running out of memory, of corrupted data files, etc.

As a general principle, the Hugin C++ API will try to recover from any error as well as possible. The API will then inform (by throwing an exception of an appropriate class) the application program of the problem and take no further action. It is then up to the application program to take the appropriate action.

When a member method fails, the data structures will always be left in a consistent state. Moreover, unless otherwise stated explicitly (either in these pages or in the Hugin API Reference Manual) for a particular method, this state can be assumed identical to the state before the failed API call.

To communicate errors to the user of the Hugin C++ API, the API defines a set of exception classes. All exception classes are subclasses of ExceptionHugin.

Examples

The following examples describe how the Hugin C++ API can be used to manipulate Bayesian networks and influence diagrams, and to perform the two different kinds of learning in the networks.

Example 1: Load And Propagate

This first example is concerned with loading a Bayesian network or an influence diagram. Once the Bayesian network or influence diagram has been loaded, the corresponding domain is triangulated using the minimum fill-in-weight heuristic and the compilation process is completed. Next, the members of each clique of the junction tree(s) are printed on standard output. Finally, a propagation of evidence is performed and the resulting posterior marginals are printed on standard output.

Note that this program loads networks saved in a "flat" network format. The Hugin GUI application saves in OOBN format by default.

# include <vector>
# include <iostream>
# include <cstdio>

# include "hugin"


using namespace HAPI;
using namespace std;


class MyParseListener : public ParseListener {
public:
  MyParseListener () {}

  void parseError (int line, const string &msg)
  {
    cerr << "Parse error location: " << line << ": " << msg << endl;
  }
};


class LAP {
public:
  LAP (const string& fileName);

  void printJunctionTrees (const JunctionTreeList& list);
  void printNodeMarginals (Domain *domain);
  void printNodes (const NodeList& list);
};


LAP::LAP (const string& fileName)
{
  try {
    MyParseListener pl;
    string netFileName = fileName + ".net";
    cout << "Opening " << netFileName << endl;
    Domain domain (netFileName, &pl);
    string logFileName = fileName + ".log";
    FILE *logFile = fopen (logFileName.c_str (), "w");
    domain.setLogFile (logFile);
    domain.triangulate (H_TM_FILL_IN_WEIGHT);
    domain.compile ();
    printJunctionTrees (domain.getJunctionTrees ());

    domain.propagate (H_EQUILIBRIUM_SUM, H_MODE_NORMAL);

    printNodeMarginals (&domain);
    fclose (logFile);

    string hkbFileName = fileName + ".hkb";
    domain.saveAsKB (hkbFileName);
  }
  catch (const ExceptionHugin& e) {
    cerr << e.what () << endl;
    throw;
  }
  catch (...) {
    cerr << "Undetermined exception caught..." << endl;
    throw;
  }
}


/**
   * Print the cliques of the junction tree (s).
   */
void LAP::printJunctionTrees (const JunctionTreeList& list)
{
  try {
    cout << "Cliques : ";

    for (JunctionTreeList::const_iterator jtit = list.begin ();
	 jtit != list.end (); jtit++) {
      CliqueList clist = (*jtit)->getCliques ();

      for (CliqueList::const_iterator cliqueit = clist.begin ();
	   cliqueit != clist.end (); cliqueit++)
	printNodes ((*cliqueit)->getMembers ());
    }

    cout << endl;
  }
  catch (const ExceptionHugin& e) {
    cerr << e.what () << endl;
    throw;
  }
}


/**
   * Print the marginal distribution of each variable in the domain.
   */
void LAP::printNodeMarginals (Domain *domain)
{
  try {
    NodeList nlist = domain->getNodes ();

    for (NodeList::const_iterator nit = nlist.begin ();
	 nit != nlist.end (); nit++) {
      Node *node = *nit;

      cout << node->getLabel () << " ("
	   << node->getName () << ")" << endl;

      if (node->getCategory () == H_CATEGORY_CHANCE) {
	if (node->getKind () == H_KIND_CONTINUOUS) {
	  ContinuousChanceNode *ccNode
	    = dynamic_cast<ContinuousChanceNode*> (node);

	  cout << "-Mean     : " << ccNode->getMean () << endl;
	  cout << "-Variance : " << ccNode->getVariance () << endl;
	}
	else if (node->getKind () == H_KIND_DISCRETE) {
	  DiscreteChanceNode *dcNode
	    = dynamic_cast<DiscreteChanceNode*> (node);
	  size_t nStates = dcNode->getNumberOfStates ();

	  for (size_t i = 0; i < nStates; i++)
	    cout << "-" << dcNode->getStateLabel (i)
		 << " " << dcNode->getBelief (i) << endl;
	}
      }
      else if (node->getCategory () == H_CATEGORY_DECISION) {
	DiscreteDecisionNode *ddNode
	  = dynamic_cast<DiscreteDecisionNode*> (node);
	size_t nStates = ddNode->getNumberOfStates ();

	for (size_t i = 0; i < nStates; i++)
	  cout << "-" << ddNode->getStateLabel (i)
	       << " " << ddNode->getExpectedUtility (i) << endl;
      }
    }
  }
  catch (const ExceptionHugin& e) {
    cerr << e.what () << endl;
    throw;
  }
}


/**
   * Print the name of each node in the list.
   */
void LAP::printNodes (const NodeList& list)
{
  try {
    for (NodeList::const_iterator nit = list.begin ();
	 nit != list.end (); nit++)
      cout << (*nit)->getName () + " ";

    cout << endl;
  }
  catch (const ExceptionHugin& e) {
    cerr << e.what () << endl;
    throw;
  }
}


/**
 * Load a Hugin net file and perform a single propagation of
 * evidence. Print the results.
 */
int main (int argc, char *argv[])
{
  if (argc < 2) {
    cerr << "Usage: " << argv[0] << " <net_file>" << endl;
    return -1;
  }

  try {
    new LAP (string (argv[1]));
  }
  catch (...) {
    return -2;
  }

  return 0;
}

Example 2: Build And Propagate

The second example describes how a Bayesian network can be constructed using the Hugin C++ API. The Bayesian network constructed consists of three numbered nodes. Two of the nodes take on values 0, 1, and 2. The third node is the sum of the two other nodes. Once the Bayesian network is constructed, the network is saved to a NET specification file and an initial propagation is performed. Finally, the marginals of the nodes are printed on standard output.

# include "hugin"

# include <vector>
# include <iostream>

using namespace HAPI;
using namespace std;

class BAP {
public:
  BAP ();
protected:
  void propagateEvidenceInNetwork ();

  void printNodeMarginals (Domain *d);

  NumberedDCNode* constructNDC (const char *label, const char *name, size_t n);

  void buildStructure
     (NumberedDCNode *A, NumberedDCNode *B, NumberedDCNode *C);

  void buildExpressionForC
     (NumberedDCNode *A, NumberedDCNode *B, NumberedDCNode *C);

  void specifyDistributions (NumberedDCNode *A, NumberedDCNode *B);

  void buildNetwork ();

  Domain *domain;

  ~BAP () { delete domain; }
};


/**
   * Build a Bayesian network and propagate evidence.
   */
BAP::BAP ()
{
  try {
    domain = new Domain ();

    buildNetwork ();

    domain->saveAsNet ("builddomain.net");
    domain->compile ();

    propagateEvidenceInNetwork ();
  }
  catch (const ExceptionHugin& e) {
    cerr << e.what () << endl;
    throw;
  }
}


/**
   * Propagate evidence in domain.
   */
void BAP::propagateEvidenceInNetwork ()
{
  try {
    domain->propagate (H_EQUILIBRIUM_SUM, H_MODE_NORMAL);

    printNodeMarginals (domain);
  }
  catch (const ExceptionHugin& e) {
    cerr << e.what () << endl;
    throw;
  }
}


/**
   * print node marginals.
   */
void BAP::printNodeMarginals (Domain *d)
{
  try {
    NodeList nlist = domain->getNodes ();

    for (NodeList::const_iterator nit = nlist.begin ();
	 nit != nlist.end (); ++nit)
    {
      DiscreteChanceNode *node = dynamic_cast<DiscreteChanceNode*> (*nit);

      if (node != 0) {
	size_t nStates = node->getNumberOfStates ();

	cout << node->getLabel () << endl;

	for (size_t i = 0; i < nStates; i++)
	  cout << "-" << node->getStateLabel (i)
	       << " " << node->getBelief (i) << endl;
      }
    }
  }
  catch (const ExceptionHugin& e) {
    cerr << e.what () << endl;
    throw;
  }
}


/**
   * Construct numbered discrete chance node.
   */
NumberedDCNode* BAP::constructNDC
 (const char *label, const char *name, size_t n)
{
  try {
    NumberedDCNode *node = new NumberedDCNode (domain);

    node->setNumberOfStates (n);

    for (size_t i = 0; i < n; i++)
      node->setStateValue (i, i);

    node->setLabel (label);
    node->setName (name);

    return node;
  }
  catch (const ExceptionHugin& e) {
    cerr << e.what () << endl;
    throw;
  }

  return NULL;
}


/**
   * Build the structure.
   */
void BAP::buildStructure
   (NumberedDCNode *A, NumberedDCNode *B, NumberedDCNode *C)
{
  try {
    C->addParent (A);
    C->addParent (B);

    A->setPosition (100, 200);
    B->setPosition (200, 200);
    C->setPosition (150, 50);
  }
  catch (const ExceptionHugin& e) {
    cerr << e.what () << endl;
    throw;
  }
}


/**
   * Expression for C
   */
void BAP::buildExpressionForC
   (NumberedDCNode *A, NumberedDCNode *B, NumberedDCNode *C)
{
  try {
    NodeList modelNodes;

    Model *model = new Model (C, modelNodes);

    NodeExpression *exprA = new NodeExpression (A);
    NodeExpression *exprB = new NodeExpression (B);

    AddExpression *exprC = new AddExpression (exprA, exprB);

    model->setExpression (0, exprC);
  }
  catch (const ExceptionHugin& e) {
    cerr << e.what () << endl;
    throw;
  }
}


/**
   * Specify the prior distribution of A and B.
   */
void BAP::specifyDistributions (NumberedDCNode *A, NumberedDCNode *B)
{
  try {
    Table *table = A->getTable ();
    vector<double> data = table->getData ();

    data[0] = 0.1;
    data[1] = 0.2;
    data[2] = 0.7;
    table->setData (data);

    table = B->getTable ();
    data = table->getData ();
    data[0] = 0.2;
    data[1] = 0.2;
    data[2] = 0.6;
    table->setData (data);
  }
  catch (const ExceptionHugin& e) {
    cerr << e.what () << endl;
    throw;
  }
}


/**
   * Build the Bayesian network.
   */
void BAP::buildNetwork ()
{
  try {
    domain->setNodeSize (50,30);

    NumberedDCNode *A = constructNDC ("A", "A", 3);
    NumberedDCNode *B = constructNDC ("B", "B", 3);

    NumberedDCNode *C = constructNDC ("C", "C", 5);

    buildStructure (A,B,C);

    buildExpressionForC (A,B,C);

    specifyDistributions (A, B);
  }
  catch (const ExceptionHugin& e) {
    cerr << e.what () << endl;
    throw;
  }
}


/**
 * Build a Bayesian network and perform a propagation of
 * evidence. Print the results.
 */
int main (int argc, char *argv[])
{
  new BAP ();
  return 0;
}

Example 3: Sequential Learning

Example 3 presents a skeleton for sequential learning. Sequential learning, or adaptation, is an update process applied to the conditional probability tables. After a network has been built, sequential learning can be applied during operation in order to maintain the correspondence between the model (conditional probability tables) and the real-world domain.

After the network is loaded in Hugin, the learning parameters are specified. Then follows the build-up and entering of cases, and finally, the tables are updated and node marginals are printed.

# include <vector>
# include <string>
# include <cstdio>
# include <iostream>
# include <exception>

# include "hugin"

using namespace HAPI;
using namespace std;

class Adapt {
public:
  Adapt (const string &fileName);

private:
  void specifyLearningParameters (Domain *d);
  void printLearningParameters (Domain *d);
  void enterCase (Domain *d);
  void printCase (Domain *d);
  void printNodeMarginals (Domain *d);
};


int main (int argc, char *argv[])
{
  try {
    new Adapt (string (argv[1]));
  }
  catch (const exception& e) {
    cerr << e.what () << endl;
    return -1;
  }
  catch (...) {
    cerr << "caught something..." << endl;
    return -2;
  }

  return 0;
}


Adapt::Adapt (const string &fileName)
{
  try {
    string netFileName = fileName + ".net";
    Domain d (netFileName, NULL);

    string logFileName = fileName + ".log";
    FILE *logFile = fopen (logFileName.c_str (), "w");
    d.setLogFile (logFile);

    d.compile ();

    specifyLearningParameters (&d);
    printLearningParameters (&d);

    enterCase (&d);

    printCase (&d);

    d.propagate ();

    d.adapt ();

    d.initialize ();

    printNodeMarginals (&d);

    d.saveAsNet ("q.net");
  }
  catch (const ExceptionHugin& eh) {
    cerr << "Caught ExceptionHugin in Adapt::Adapt ()." << endl;
    cerr << eh.what () << endl;
    throw;
  }
  catch (const exception& e) {
    cerr << "Caught general exception in Adapt::Adapt ()." << endl;
    cerr << e.what () << endl;
    throw;
  }
}


void Adapt::specifyLearningParameters (Domain *d)
{
  NodeList nl = d->getNodes ();
  vector<double> data;

  try {
    for (NodeList::const_iterator nlIter = nl.begin (), nlEnd = nl.end ();
	 nlIter != nlEnd; ++nlIter) {
      DiscreteChanceNode *node = dynamic_cast<DiscreteChanceNode*> (*nlIter);

      if (node != 0) {
	Table *table = node->getExperienceTable ();

	data.clear ();
	data.insert (data.end (), table->getSize (), 1);

	table->setData (data);
      }
    }
  }
  catch (const ExceptionHugin& eh) {
    cerr << "Caught ExceptionHugin" << endl;
    cerr << "Filling experience tables in Adapt::specifyLearningParameters ()"
         << endl;
    cerr << eh.what () << endl;
    throw;
  }
  catch (const exception& e) {
    cerr << "Caught general exception" << endl;
    cerr << "Filling experience tables in Adapt::specifyLearningParameters ()"
         << endl;
    cerr << e.what () << endl;
    throw;
  }

  try {
    for (NodeList::const_iterator nlIter = nl.begin (), nlEnd = nl.end ();
	 nlIter != nlEnd; ++nlIter) {
      DiscreteChanceNode *node = dynamic_cast<DiscreteChanceNode*> (*nlIter);

      if (node != 0) {
	Table *table = node->getFadingTable ();

	data.clear ();
	data.insert (data.end (), table->getSize (), 1);

	table->setData (data);
      }
    }
  }
  catch (const ExceptionHugin& eh) {
    cerr << "Caught ExceptionHugin in Adapt::specifyLearningParameters ()"
         << endl;
    cerr << "Filling fading tables in Adapt::specifyLearningParameters ()"
         << endl;
    cerr << eh.what () << endl;
    throw;
 }
 catch (const exception& e) {
    cerr << "General exception in Adapt::specifyLearningParameters ()"
         << endl;
    cerr << "Filling fading tables in Adapt::specifyLearningParameters ()"
         << endl;
    cerr << e.what () << endl;
    throw;
  }
}


void Adapt::printLearningParameters (Domain *d)
{
  try {
    NodeList nl = d->getNodes ();

    for (NodeList::const_iterator nlIter = nl.begin (), nlEnd = nl.end ();
	 nlIter != nlEnd; ++nlIter) {
      DiscreteChanceNode *dcNode = dynamic_cast<DiscreteChanceNode*> (*nlIter);

      if (dcNode != 0) {
	cout << dcNode->getLabel () << " (" << dcNode->getName ()
             << "): " << endl;

	cout << "   ";
	if (dcNode->hasExperienceTable ()) {
	  Table *table = dcNode->getExperienceTable ();
	  vector<double> data = table->getData ();
	  size_t tblSize = table->getSize ();

	  for (size_t i = 0; i < tblSize; i++)
	    cout << data[i] << " ";

	  cout << endl;
	}
	else
	  cout << "No experience table" << endl;

	cout << "   ";
	if (dcNode->hasFadingTable ()) {
	  Table *table = dcNode->getFadingTable ();
	  vector<double> data = table->getData ();
	  size_t tblSize = table->getSize ();

	  for (size_t i = 0; i < tblSize; i++)
	    cout << data[i] << " ";

	  cout << endl;
	}
	else
	  cout << "No fading table" << endl;
      }
    }
  }
  catch (const ExceptionHugin& eh) {
    cerr << "Caught ExceptionHugin in Adapt::printLearningParameters ()."
         << endl;
    cerr << eh.what () << endl;
    throw;
  }
  catch (const exception& e) {
    cerr << "Caught general exception in Adapt::printLearningParameters ()."
         << endl;
    cerr << e.what () << endl;
    throw;
  }
}


void Adapt::enterCase (Domain *d)
{
  try {
    NodeList nl = d->getNodes ();

    for (NodeList::const_iterator nlIter = nl.begin (), nlEnd = nl.end ();
	 nlIter != nlEnd; ++nlIter) {
      DiscreteChanceNode *dcNode = dynamic_cast<DiscreteChanceNode*> (*nlIter);

      if (dcNode != 0)
	dcNode->selectState (0);
    }

    DiscreteChanceNode *dcNode = dynamic_cast<DiscreteChanceNode*> (nl[1]);

    if (dcNode != 0)
      dcNode->retractFindings ();
  }
  catch (const ExceptionHugin& eh) {
    cerr << "Caught ExceptionHugin in Adapt::enterCase ()" << endl;
    cerr << eh.what () << endl;
    throw;
  }
  catch (const exception& e) {
    cerr << "Caught general exception in Adapt::enterCase ()" << endl;
    cerr << e.what () << endl;
    throw;
  }
}


void Adapt::printCase (Domain *d)
{
  try {
    NodeList nl = d->getNodes ();

    for (NodeList::const_iterator nlIter = nl.begin (), nlEnd = nl.end ();
	 nlIter != nlEnd; ++nlIter) {
      DiscreteChanceNode *dcNode = dynamic_cast<DiscreteChanceNode*> (*nlIter);

      if (dcNode != 0) {
	cout << " (" + dcNode->getName () + ",";
	if (dcNode->isEvidenceEntered ())
	  cout << " evidence is entered) ";
	else
	  cout << " evidence is not entered) ";
      }
    }
    cout << endl;
  }
  catch (const ExceptionHugin& eh) {
    cerr << "Caught ExceptionHugin in Adapt::printCase ()" << endl;
    cerr << eh.what () << endl;
    throw;
  }
  catch (const exception& e) {
    cerr << "Caught general exception in Adapt::printCase ()" << endl;
    cerr << e.what () << endl;
    throw;
  }
}


void Adapt::printNodeMarginals (Domain *d)
{
  try {
    NodeList nl = d->getNodes ();

    for (NodeList::const_iterator nlIter = nl.begin (), nlEnd = nl.end ();
	 nlIter != nlEnd; ++nlIter) {
      DiscreteChanceNode *dcNode = dynamic_cast<DiscreteChanceNode*> (*nlIter);

      if (dcNode != 0) {
	size_t nStates = dcNode->getNumberOfStates ();

	cout << dcNode->getLabel () + " (" + dcNode->getName () + ")" << endl;

	for (size_t i = 0; i < nStates; i++)
	  cout << " - " << dcNode->getStateLabel (i)
	       << ": " << dcNode->getBelief (i) << endl;
      }
    }
  }
  catch (const ExceptionHugin& eh) {
    cerr << "Caught ExceptionHugin in Adapt::printNodeMarginals ()" << endl;
    cerr << eh.what () << endl;
    throw;
  }
  catch (const exception& e) {
    cerr << "Caught general exception in Adapt::printNodeMarginals ()" << endl;
    cerr << e.what () << endl;
    throw;
  }
}

Example 4: Parametric Learning

The fourth example shows how the Hugin C++ API can be used for parametric learning in a Bayesian network. The network is loaded from disk, and the parameters controlling the learning process are loaded. Then, the conditional probability tables are computed from data using the EM algorithm. Finally, the node marginals are printed.

# include <vector>
# include <string>
# include <cstdio>
# include <iostream>
# include <exception>

# include "hugin"

using namespace HAPI;
using namespace std;

class EM {
public:
  EM (const string &fileName);

private:
  void specifyLearningParameters (Domain *d);
  void printLearningParameters (Domain *d);
  void loadCases (Domain *d);
  void printCases (Domain *d);
  void printNodeMarginals (Domain *d);
};


int main (int argc, char *argv[])
{
  try {
    new EM (string (argv[1]));
  }
  catch (const exception& e) {
    cerr << e.what () << endl;
    return -1;
  }
  catch (...) {
    cerr << "caught something..." << endl;
    return -2;
  }
  return 0;
}


EM::EM (const string &fileName)
{
  try {
    string netFileName = fileName + ".net";
    Domain d (netFileName, NULL);

    string logFileName = fileName + ".log";
    FILE *logFile = fopen (logFileName.c_str (), "w");
    d.setLogFile (logFile);

    d.compile ();

    specifyLearningParameters (&d);
    printLearningParameters (&d);

    loadCases (&d);
    printCases (&d);

    d.learnTables ();
    d.setNumberOfCases (0); // ??
    cout << "Log likelihood: " << d.getLogLikelihood () << endl;

    d.initialize ();

    d.propagate ();

    printNodeMarginals (&d);

    d.saveAsNet ("q.net");
  }
  catch (const ExceptionHugin& eh) {
    cerr << "Caught ExceptionHugin in EM::EM ()." << endl;
    cerr << eh.what () << endl;
    throw;
  }
  catch (const exception& e) {
    cerr << "Caught general exception in EM::EM ()." << endl;
    cerr << e.what () << endl;
    throw;
  }
}


void EM::specifyLearningParameters (Domain *d)
{
  try {
    const NodeList nl = d->getNodes ();

    vector<double> data;

    for (NodeList::const_iterator nlIter = nl.begin (), nlEnd = nl.end ();
	 nlIter != nlEnd; ++nlIter) {
      DiscreteChanceNode *node = dynamic_cast<DiscreteChanceNode*> (*nlIter);

      if (node != 0) {
	Table *table = node->getExperienceTable ();

	data.clear ();
	data.insert (data.end (), table->getSize (), 1);

	table->setData (data);
      }
    }

    d->setLogLikelihoodTolerance (0.000001);
    d->setMaxNumberOfEMIterations (1000);
  }
  catch (const ExceptionHugin& eh) {
    cerr << "Caught ExceptionHugin" << endl;
    cerr << "Filling experience tables in EM::specifyLearningParameters (Domain *d)"
         << endl;
    cerr << eh.what () << endl;
    throw;
  }
  catch (const exception& e) {
    cerr << "Caught general exception" << endl;
    cerr << "Filling experience tables in EM::specifyLearningParameters (Domain *d)"
         << endl;
    cerr << e.what () << endl;
    throw;
  }
}


void EM::printLearningParameters (Domain *d)
{
  try {
    const NodeList nl = d->getNodes ();

    for (NodeList::const_iterator nlIter = nl.begin (), nlEnd = nl.end ();
	 nlIter != nlEnd; ++nlIter) {
      DiscreteChanceNode *dcNode = dynamic_cast<DiscreteChanceNode*> (*nlIter);

      if (dcNode != 0) {
	cout << dcNode->getLabel () << " (" << dcNode->getName () << "): "
             << endl;

	cout << "   ";
	if (dcNode->hasExperienceTable ()) {
	  Table *table = dcNode->getExperienceTable ();
	  vector<double> data = table->getData ();
	  size_t tblSize = table->getSize ();

	  for (size_t i = 0; i < tblSize; i++)
	    cout << data[i] << " ";

	  cout << endl;
	}
	else
	  cout << "No experience table" << endl;

	cout << "   ";
	if (dcNode->hasFadingTable ()) {
	  Table *table = dcNode->getFadingTable ();
	  vector<double> data = table->getData ();
	  size_t tblSize = table->getSize ();

	  for (size_t i = 0; i < tblSize; i++)
	    cout << data[i] << " ";

	  cout << endl;
	}
	else
	  cout << "No fading table" << endl;
      }
    }

    cout << "Log likelihood tolerance: " << d->getLogLikelihoodTolerance ()
         << endl;
    cout << "Max EM iterations: " << d->getMaxNumberOfEMIterations () << endl;
  }
  catch (const ExceptionHugin& eh) {
    cerr << "Caught ExceptionHugin." << endl;
    cerr << eh.what () << endl;
    throw;
  }
  catch (const exception& e) {
    cerr << "Caught general exception." << endl;
    cerr << e.what () << endl;
    throw;
  }
}


void EM::loadCases (Domain *d)
{
  try {
    d->setNumberOfCases (0);

    size_t iCase = d->newCase ();
    cout << "Case index: " << iCase << endl;

    d->setCaseCount (iCase, 2.5);

    const NodeList nl = d->getNodes ();

    for (NodeList::const_iterator nlIter = nl.begin (), nlEnd = nl.end ();
	 nlIter != nlEnd; ++nlIter) {
      DiscreteChanceNode *dcNode = dynamic_cast<DiscreteChanceNode*> (*nlIter);

      if (dcNode != 0)
	dcNode->setCaseState (iCase, 0);
    }

    DiscreteChanceNode *dcNode = dynamic_cast<DiscreteChanceNode*> (nl[1]);
    if (dcNode != 0)
      dcNode->unsetCase (iCase);
  }
  catch (const ExceptionHugin& eh) {
    cerr << "Caught ExceptionHugin in EM::enterCase (Domain *d)" << endl;
    cerr << eh.what () << endl;
    throw;
  }
  catch (const exception& e) {
    cerr << "Caught general exception in EM::enterCase (Domain *d)" << endl;
    cerr << e.what () << endl;
    throw;
  }
}


void EM::printCases (Domain *d)
{
  try {
    const NodeList nl = d->getNodes ();

    size_t nCases = d->getNumberOfCases ();

    cout << "Number of cases: " << nCases << endl;

    for (size_t i = 0; i < nCases; i++) {
      cout << "case " << i << " " << d->getCaseCount (i) << " " << endl;

      for (NodeList::const_iterator nlIter = nl.begin (), nlEnd = nl.end ();
	   nlIter != nlEnd; ++nlIter) {
	DiscreteChanceNode *dcNode = dynamic_cast<DiscreteChanceNode*> (*nlIter);

	if (dcNode != 0) {
	  cout << " (" + dcNode->getName () + ",";
	  if (dcNode->caseIsSet (i))
            cout << dcNode->getCaseState (i) << ") ";
	  else
            cout << "N/A) ";
	}
      }
    }
    cout << endl;
  }
  catch (const ExceptionHugin& eh) {
    cerr << "Caught ExceptionHugin in EM::printCase (Domain *d)" << endl;
    cerr << eh.what () << endl;
    throw;
  }
  catch (const exception& e) {
    cerr << "Caught general exception in EM::printCase (Domain *d)" << endl;
    cerr << e.what () << endl;
    throw;
  }
}


void EM::printNodeMarginals (Domain *d)
{
  try {
    const NodeList nl = d->getNodes ();

    for (NodeList::const_iterator nlIter = nl.begin (), nlEnd = nl.end ();
	 nlIter != nlEnd; ++nlIter) {
      DiscreteChanceNode *dcNode = dynamic_cast<DiscreteChanceNode*> (*nlIter);

      if (dcNode != 0) {
	size_t nStates = dcNode->getNumberOfStates ();

	cout << dcNode->getLabel () + " (" + dcNode->getName () + ")" << endl;

	for (size_t i = 0; i < nStates; i++)
	  cout << " - " << dcNode->getStateLabel (i)
	       << ": " << dcNode->getBelief (i) << endl;
      }
    }
  }
  catch (const ExceptionHugin& eh) {
    cerr << "Caught ExceptionHugin in EM::printNodeMarginals (Domain *d)"
         << endl;
    cerr << eh.what () << endl;
    throw;
  }
  catch (const exception& e) {
    cerr << "Caught general exception in EM::printNodeMarginals (Domain *d)"
         << endl;
    cerr << e.what () << endl;
    throw;
  }
}

Example 5: Object Oriented Networks

The last example demonstrates the Object Oriented network facilities of the Hugin C++ API. It starts out by creating two very simple networks. Creates an instance of one network in the other. Then it uses the input and output nodes in the instance to connect the two networks, and creates a runtime domain from the class. It ends by printing the origin of all the nodes in the domain.

# include "hugin"
# include <cstdio>
# include <iostream>

using namespace HAPI;
using namespace std;

class ClassBuildInstance {
public:
  int test ();
};

/* Build the first network. This will contain an
   instance of the second network
*/
void buildFirst (Class* cls)
{
  LabelledDCNode *node1 = new LabelledDCNode (cls);
  node1->setName ("c1_n1");
  node1->setNumberOfStates (3);

  LabelledDCNode *node2 = new LabelledDCNode (cls);
  node2->setName ("c1_n2");
  node2->setNumberOfStates (2);

  LabelledDCNode *node3 = new LabelledDCNode (cls);
  node3->setName ("c1_n3");
  node3->setNumberOfStates (3);

  node2->addParent (node1);
  node3->addParent (node2);
}

/* Build the second network to be instantiated in
   the first network
*/
void buildSecond (Class* cls)
{
  LabelledDCNode *node1 = new LabelledDCNode (cls);
  node1->setName ("c2_n1");
  node1->setNumberOfStates (3);

  LabelledDCNode *node2 = new LabelledDCNode (cls);
  node2->setName ("c2_n2");
  node2->setNumberOfStates (2);

  LabelledDCNode *node3 = new LabelledDCNode (cls);
  node3->setName ("c2_n3");
  node3->setNumberOfStates (3);

  node3->addParent (node1);
  node3->addParent (node2);

  // make node3 output node
  node3->addToOutputs ();
  // make node2 input node
  // note that only nodes with no parents can be input node
  node2->addToInputs ();
}


int ClassBuildInstance::test ()
{
  try {
    // create the class collection to contain the classes
    ClassCollection coll;

    // create the first class in the collection
    Class *cls1 = new Class (&coll);
    cls1->setName ("c1");
    buildFirst (cls1);

    // create the second class in the collection
    Class *cls2 = new Class (&coll);
    cls2->setName ("c2");
    buildSecond (cls2);

    cout << "----------------------------------------\n";
    cout << "Testing instances\n";
    cout << "----------------------------------------\n";
    // create an instance of cls2 in cls1
    InstanceNode *instance = new InstanceNode (cls1, cls2);
    cout << "Instance " << instance->getName () << " derived from "
         << instance->getClass ()->getName () << endl;

    {
      cout << "\n----------------------------------------\n";
      cout << "Testing outputs and clones\n";
      cout << "----------------------------------------\n";
      // get the output node from cls2
      Node *node = cls2->getNodeByName ("c2_n3");

      // we will add the clone of the output as parent to c1_n2
      DiscreteChanceNode *node2
	= dynamic_cast<DiscreteChanceNode*>(cls1->getNodeByName ("c1_n2"));
      // instance->getOutput retrieves the output clone for the given node
      node2->addParent (dynamic_cast<DiscreteChanceNode*>
			(instance->getOutput (node)));

      cout << "Removing output \n";
      // removing the c2_n3 from the output list. This will
      // delete the output clone, so that c1_n2 no longer has that as parent.
      node->removeFromOutputs ();
      cout << "Done \n";
    }

    {
      cout << "\n----------------------------------------\n";
      cout << "Testing inputs and bindings\n";
      cout << "----------------------------------------\n";
      // get the first (and only) input node from cls2
      Node *node = cls2->getInputs ().front ();
      Node *node2 = cls1->getNodeByName ("c1_n2");
      // bind c1_n2 to the input node. This effectively replaces
      // the table of the input node with that of the bound node.
      instance->setInput (node, node2);
      cout << "Bound " << node->getName () << " to "
	   << instance->getInput (node)->getName () << endl;
    }

    { // create a domain to perform propagation
      cout << "\n----------------------------------------\n";
      cout << "Testing domain creation\n";
      cout << "----------------------------------------\n";
      Domain *dom = cls1->createDomain ();

      // print out the origin of all nodes in the domain
      NodeList nodes = dom->getNodes ();
      for (size_t j = 0; j < nodes.size (); j++) {
	cout << nodes[j]->getName () << " comes from ";
	NodeList list = nodes[j]->getSource ();
	for (size_t i = 0; i < list.size (); i++)
	  cout << list[i]->getName () << (i + 1 == list.size () ? "\n" : ".");
      }
      dom->saveAsNet ("cbap.net");
    }

    coll.saveAsNet ("cbColl.net");
  }
  catch (const ExceptionHugin& e) {
    cerr << "Caught exception\n";
    cerr << e.what () << endl;
    return 1;
  }
  return 0;
}

int main ()
{
  ClassBuildInstance *cbi = new ClassBuildInstance ();
  return cbi->test ();
}

Copyright Hugin Expert A/S 1993-2004