Hugin .NET API Reference Library
Namespaces
Namespaces
Namespaces
NamespaceDescription
HAPI
The HUGIN .NET API namespace.
Remarks

Description of HUGIN .NET API

The HUGIN .NET API consists of the HAPI namespace. These pages are meant as a help for developers programming against the HUGIN .NET API. 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 Reference Manual will be a good place to look. It contains detailed documentation of the HUGIN C API, which is the basis of the HUGIN .NET API. The HUGIN API Reference Manual can be downloaded from HUGIN Expert A/S - Documentation.

This document contains the following:

General Information

The HUGIN .NET API contains a high performance inference engine that can be used as the core of knowledge based systems built using Bayesian networks or limited memory influence diagrams (LIMIDs). Using probabilistic descriptions of causal relationships in an application domain, a knowledge engineer can build knowledge bases that model the domain. Given this description, the HUGIN Decision Engine can perform fast and accurate reasoning.

The HUGIN .NET API is organized as an ordinary .NET namespace. Classes and member methods are provided for tasks such as construction of networks, performing inference, etc. The HUGIN .NET API also provides an exception based mechanism for handling errors.

The Microsoft .NET Framework v. 2.0 or v. 4.0 is required by the HUGIN .NET API: http://msdn.microsoft.com/netframework/

.NET CLR 4.0 dependency note: The HUGIN .NET API for .NET CLR 4.0 has a dependency on the Microsoft Visual C++ 2010 Runtime.

Download and install Microsoft Visual C++ 2010 Runtime:

For support please see our new Developer Forum http://forum.hugin.com

Object-Oriented Specification

The HUGIN .NET API provides a powerful object-orientation mechanism. Object-oriented specification of Bayesian networks and LIMIDs make it very easy to reuse models, to encapsulate submodels (providing a means for hierarchical model specification), and to perform model construction in a top-down fashion, a bottom-up fashion, or a mix of the two (allowing repeated changes of level of abstraction).

The classes Domain and Class both extend the abstract class NetworkModel, which contain enums and methods common to regular Bayesian networks (and LIMIDs) and object-oriented Bayesian networks (and LIMIDs), respectively.

In addition to the usual nodes, an object-oriented Bayesian network or LIMID contains instance nodes, representing instances of other networks. In other words, an instance node represents a subnetwork. Of course, the network of which instances exist in other networks can itself contain instance nodes, whereby an object-oriented network can be viewed as a hierarchical description of a problem domain. Describing a network in a hierarchical fashion often makes the network much less cluttered, and thus provides a much better means of communicating ideas among knowledge engineers and users.

As systems often are composed of collections of identical or similar components, models of systems often contain repetitive patterns. The notion of instance nodes makes it very easy to construct multiple identical instances of a network fragment.

Memory Management

The HUGIN .NET API is comprised of two parts: a managed part written in C# and an unmanaged part written in VC++. Contrary to managed objects, unmanaged data objects are not automatically reclaimed (or garbage collected) when a program stops referring to them. Therefore, some explicit memory management is needed in order to avoid memory leaks.

For example, when a managed Domain object is no longer needed, it should be deleted. Deleting a Domain object will cause all the unmanaged objects associated with the Domain object to be implicitly deleted: the unmanaged domain object, the unmanaged node objects, etc. At the same time all managed objects corresponding to those unmanaged objects are marked "dead" (that is, invoking methods on such objects will cause ExceptionObjectNotAlive exceptions). When the managed program stops referring to dead objects, the garbage collector can reclaim them.

Only objects that are not "owned by" other objects require explicit deletion. (For example, nodes are owned by a Domain or a Class object; a Class object is owned by a ClassCollection object; etc.) An object owned by another object is automatically deleted when its owner is deleted.

The following objects are not owned by other objects and must therefore be explicitly deleted in the managed code:

Error Handling

Several types of errors can occur when using a class or member method from the HUGIN .NET 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 .NET API will try to recover from any error as well as possible. The API will then inform 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 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 .NET API, the API defines a set of exception classes. All exception classes are subclasses of ExceptionHugin.

Using the HUGIN .NET API

The HUGIN .NET API consists of eight different dll files. For each .NET Common Language Runtime 2.0 and 4.0, there are two files for the x86 platform, and two files for the x64 platform:
Name of dll fileSpecification
hugincs-8.5-2.0.dllSingle precision, API version 8.5, .NET CLR 2.0, 32-bit (x86)
hugincs2-8.5-2.0.dllDouble precision, API version 8.5, .NET CLR 2.0, 32-bit (x86)
hugincs-8.5-4.0.dllSingle precision, API version 8.5, .NET CLR 4.0, 32-bit (x86)
hugincs2-8.5-4.0.dllDouble precision, API version 8.5, .NET CLR 4.0, 32-bit (x86)
hugincs-8.5-2.0-x64.dllSingle precision, API version 8.5, .NET CLR 2.0, 64-bit (x64)
hugincs2-8.5-2.0-x64.dllDouble precision, API version 8.5, .NET CLR 2.0, 64-bit (x64)
hugincs-8.5-4.0-x64.dllSingle precision, API version 8.5, .NET CLR 4.0, 64-bit (x64)
hugincs2-8.5-4.0-x64.dllDouble precision, API version 8.5, .NET CLR 4.0, 64-bit (x64)
Note that the HUGIN .NET API only supports the 32-bit x86-compatible common language runtime, and the 64-bit common language runtime on a computer that supports the AMD64 or EM64T instruction set. The 64-bit common language runtime on a computer with an Itanium processor is not supported.

.NET CLR 4.0 dependency note: The HUGIN .NET API for .NET CLR 4.0 has a dependency on the Microsoft Visual C++ 2010 Runtime.

Download and install Microsoft Visual C++ 2010 Runtime:

Important:

Return and parameter types vary between the four dlls. In specific this relates to certain methods in following classes and interfaces:

A generic signature is displayed in the brief describtion field of any methods that vary in return and parameter type throughout this manual.

On 32-bit size_t = uint; h_index_t = int. On 64-bit size_t = ulong; h_index_t = long. Single precision h_number_t = single. Double precision h_number_t = double.
DiscreteNode
public h_number_t GetPredictedBelief(size_t state, size_t time)
public void ComputeExplanationData(size_t x, DiscreteNode Y, size_t y, size_t maxSubsetSize)
public void ComputeExplanationData(size_t state, size_t maxSubsetSize)
public void EnterFinding(size_t state, h_number_t finding)
public h_number_t GetEnteredFinding(size_t state)
public size_t GetNumberOfStates()
public h_number_t GetPropagatedFinding(size_t state)
public size_t GetSampledState()
public h_index_t GetStateIndex(String label)
public String GetStateLabel(size_t state)
public void SelectState(size_t state)
public void SetNumberOfStates(size_t newNumber)
public void SetStateLabel(size_t state, String newLabel)
DiscreteChanceNode
public void ComputeSensitivityData(size_t state)
public h_number_t GetBelief(size_t state)
public h_number_t[] GetSensitivityConstants(size_t index)
public size_t GetCaseState(uint c)
public void SetCaseState(size_t c, size_t state)
public void UnsetCase(size_t c)
NumericDiscreteNode
size_t GetStateIndex(double value)
double GetStateValue(size_t s)
void SetStateValue(size_t s, double v)
DiscreteDecisionNode
public h_number_t GetExpectedUtility(size_t state)
ContinuousChanceNode
public bool CaseIsSet(size_t c)
public double GetAlpha(size_t i)
public double GetBeta(ContinuousChanceNode parent, size_t i)
public double GetCaseValue(size_t c)
public double GetGamma(size_t i)
public void SetAlpha(double alpha, size_t i)
public void SetBeta(double beta, ContinuousChanceNode parent, size_t i)
public void SetCaseValue(size_t c, double value)
public void SetGamma(double gamma, size_t i)
public void UnsetCase(size_t c)
ParseListener
void ParseError(size_t position, String msg)
Domain
public size_t GetDBNWindowOffset()
public void MoveDBNWindow(size_t delta)
public void ComputeDBNPredictions(size_t numberOfTimePoints)
public void EnterCase(size_t caseIndex)
public h_number_t GetCaseCount(size_t caseIndex)
public size_t GetConcurrencyLevel()
public double GetConflict()
public size_t GetGrainSize()
public size_t GetMaxNumberOfEMIterations()
public size_t GetMaxNumberOfSeparators()
public size_t GetNumberOfCases()
public size_t NewCase()
public void SaveCases(String fileName, NodeList nodes, size_t[] cases, bool caseCounts, String separator, String missingData)
public void SetCaseCount(size_t caseIndex, h_number_t caseCount)
public void SetConcurrencyLevel(size_t level)
public void SetGrainSize(size_t size)
public void SetMaxNumberOfEMIterations(size_t count)
public void SetMaxNumberOfSeparators(size_t count)
public void SetNumberOfCases(size_t count)
public h_count_t GetNumberOfMAPConfigurations()
public size_t[] GetMAPConfiguration(size_t index)
public double GetProbabilityOfMAPConfiguration(size_t index)
public h_count_t GetNumberOfExplanations()
public NodeList GetExplanation(size_t rank)
public h_number_t GetExplanationScore(size_t index)
Model
public Expression GetExpression(size_t index)
public size_t GetNumberOfSamplesPerInterval()
public size_t GetSize()
public void SetExpression(size_t index, Expression expression)
public void SetExpression(size_t index, String expression, ParseListener parseListener)
public void SetNumberOfSamplesPerInterval(size_t count)
Table
public double GetCovariance(size_t i, ContinuousChanceNode node1, ContinuousChanceNode node2)
public h_number_t[] GetData()
public void GetData(ref h_number_t[] data, size_t start, size_t count)
public h_number_t GetDataItem(size_t index)
public double GetMean(size_t i, ContinuousChanceNode node)
public size_t GetSize()
public double GetVariance(size_t i, ContinuousChanceNode node)
public void SetData(h_number_t[] newData)
public void SetData(h_number_t[] data, size_t start, size_t count)
public void SetDataItem(size_t index, h_number_t value)
public void GetConfiguration(ref size_t[] configuration, size_t index)
public size_t GetIndex(size_t[] configuration)
public size_t GetCGSize()
JunctionTree
public size_t GetTotalCGSize()
public size_t GetTotalSize()

In order to make switching between the different HUGIN .NET API versions easier, it is recommended to declare aliases for h_number_t, size_t and h_index_t in the appropriate source files and use these aliases as types. This can be done by placing the following piece of code at the beginning of the source files (and then use the proper size_t, h_index_t and h_number_t types in the source code):

#if X64
using size_t = System.UInt64;
using h_index_t = System.Int64;
#else
using size_t = System.UInt32;
using h_index_t = System.Int32;
#endif
#if H_DOUBLE
using h_number_t = System.Double;
#else
using h_number_t = System.Single;
#endif
          
Define symbol H_DOUBLE to compile with double precision and leave it out to compile with single precision. And define symbol X64 when compiling for 64-bit and leave it out when compiling for 32-bit.

Examples

The following section gives examples of HUGIN .NET API usage. All examples use the recommended type aliasing scheme.

Example 1: Load And Propagate

This first example is concerned with loading a Bayesian network or a LIMID. Once the Bayesian network or LIMID has been loaded, the corresponding domain is triangulated using the minimum fill-in-weight heuristic and the compilation process is completed. Next, the beliefs and utilities are printed. Finally, a HUGIN case file is entered and a propagation of evidence is performed and the new beliefs and utilities are printed.

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

#if X64
using size_t = System.UInt64;
using h_index_t = System.Int64;
#else
using size_t = System.UInt32;
using h_index_t = System.Int32;
#endif
#if H_DOUBLE
using h_number_t = System.Double;
#else
using h_number_t = System.Single;
#endif

using System;
using HAPI;

namespace Example
{
    public class LoadAndPropagate
    {
        /// <summary>
        /// This function parses the given NET file, compiles the network,
        /// and prints the prior beliefs and expected utilities of all
        /// nodes.  If a case file is given, the function loads the file,
        /// propagates the evidence, and prints the updated results.
        /// 
        /// If the network is a LIMID, we assume that we should compute
        /// policies for all decisions (rather than use the ones specified
        /// in the NET file).  Likewise, we update the policies when new
        /// evidence arrives.
        /// </summary<
        public static void LAP(String netName, String caseName)
        {
            try
            {
                ParseListener parseListener = new DefaultClassParseListener();
                Domain domain = new Domain(netName + ".net", parseListener);
                domain.OpenLogFile(netName + ".log");
                domain.Triangulate(Domain.TriangulationMethod.H_TM_BEST_GREEDY);
                domain.Compile();
                domain.CloseLogFile();

                bool hasUtilities = ContainsUtilities(domain.GetNodes());

                if (!hasUtilities)
                    Console.WriteLine("Prior beliefs:");
                else
                {
                    domain.UpdatePolicies();
                    Console.WriteLine("Overall expected utility: "
                                + domain.GetExpectedUtility());
                    Console.WriteLine();
                    Console.WriteLine("Prior beliefs (and expected utilities):");
                }
                PrintBeliefsAndUtilities(domain);

                if (caseName != null)
                {
                    domain.ParseCase(caseName, parseListener);

                    Console.WriteLine();
                    Console.WriteLine();
                    Console.WriteLine("Propagating the evidence specified in \""
                                + caseName + "\"");

                    domain.Propagate(Domain.Equilibrium.H_EQUILIBRIUM_SUM,
                              Domain.EvidenceMode.H_EVIDENCE_MODE_NORMAL);

                    Console.WriteLine();
                    Console.WriteLine("P(evidence) = "
                                + domain.GetNormalizationConstant());
                    Console.WriteLine();

                    if (!hasUtilities)
                        Console.WriteLine("Updated beliefs:");
                    else
                    {
                        domain.UpdatePolicies();
                        Console.WriteLine("Overall expected utility: "
                                + domain.GetExpectedUtility());
                        Console.WriteLine();
                        Console.WriteLine("Updated beliefs (and expected utilities):");
                    }
                    PrintBeliefsAndUtilities(domain);
                }

                domain.Delete();
            }
            catch (ExceptionHugin e)
            {
                Console.WriteLine("Exception caught: " + e.Message);
            }
            catch (Exception e)
            {
                Console.WriteLine("General exception: " + e.Message);
                Console.WriteLine(e.StackTrace.ToString());
            }
        }


        /// <summary>
        /// Print the beliefs and expected utilities of all nodes in the domain.
        /// </summary>
        public static void PrintBeliefsAndUtilities(Domain domain)
        {
            NodeList nodes = domain.GetNodes();
            bool hasUtilities = ContainsUtilities(nodes);
            foreach (Node node in nodes)
            {
                Console.WriteLine();
                Console.WriteLine(node.GetLabel() + " (" + node.GetName() + ")");

                if (node is UtilityNode)
                    Console.WriteLine("  - Expected utility: "
                                + ((UtilityNode)node).GetExpectedUtility());
                else if (node is DiscreteNode)
                {
                    DiscreteNode dNode = (DiscreteNode)node;

                    for (size_t i = 0, n = dNode.GetNumberOfStates(); i < n; i++)
                    {
                        Console.WriteLine("  - " + dNode.GetStateLabel(i)
                                  + " " + dNode.GetBelief(i));
                        if (hasUtilities)
                            Console.WriteLine(" (" + dNode.GetExpectedUtility(i) + ")");
                        else
                            Console.WriteLine();
                    }
                }
                else if (node is FunctionNode)
                {
                    FunctionNode fNode = (FunctionNode)node;
                    try {
                        double value = fNode.GetValue();
                        Console.WriteLine("  - Function value : " + value);
                    } catch (ExceptionHugin) {
                        Console.WriteLine("  - Function value : N/A");
                    }
                }
                else
                {
                    ContinuousChanceNode ccNode = (ContinuousChanceNode)node;

                    Console.WriteLine("  - Mean : " + ccNode.GetMean());
                    Console.WriteLine("  - SD   : " + Math.Sqrt(ccNode.GetVariance()));
                }
            }
        }


        /// <summary>
        /// Are there utility nodes in the list?
        /// </summary>
        public static bool ContainsUtilities(NodeList list)
        {
            foreach (Node node in list)
            {
                if (node is UtilityNode)
                    return true;
            }

            return false;
        }


        /// <summary>
        /// Load a HUGIN NET file, compile the network, and print the
        /// results.  If a case file is specified, load it, propagate the
        /// evidence, and print the results.
        /// </summary>
        public static void Main(String[] args)
        {
            if (args.Length == 1)
                LAP(args[0], null);
            else if (args.Length == 2)
                LAP(args[0], args[1]);
            else
                Console.Error.WriteLine("Usage: <netName> [<caseName>]");
        }

    }
}
        
Compiling:

.NET CLR 2.0, 32-bit, single precision: csc LoadAndPropagate.cs /platform:x86 /r:hugincs-8.5-2.0.dll

.NET CLR 2.0, 32-bit, double precision: csc LoadAndPropagate.cs /platform:x86 /r:hugincs2-8.5-2.0.dll /d:H_DOUBLE

.NET CLR 2.0, 64-bit, single precision: csc LoadAndPropagate.cs /platform:x64 /r:hugincs-8.5-2.0-x64.dll /d:X64

.NET CLR 2.0, 64-bit, double precision: csc LoadAndPropagate.cs /platform:x64 /r:hugincs2-8.5-2.0-x64.dll /d:X64,H_DOUBLE

.NET CLR 4.0, 32-bit, single precision: csc LoadAndPropagate.cs /platform:x86 /r:hugincs-8.5-4.0.dll

.NET CLR 4.0, 32-bit, double precision: csc LoadAndPropagate.cs /platform:x86 /r:hugincs2-8.5-4.0.dll /d:H_DOUBLE

.NET CLR 4.0, 64-bit, single precision: csc LoadAndPropagate.cs /platform:x64 /r:hugincs-8.5-4.0-x64.dll /d:X64

.NET CLR 4.0, 64-bit, double precision: csc LoadAndPropagate.cs /platform:x64 /r:hugincs2-8.5-4.0-x64.dll /d:X64,H_DOUBLE

Example 2: Build And Propagate

The second example describes how a Bayesian network can be constructed using the HUGIN .NET 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.

#if X64
using size_t = System.UInt64;
using h_index_t = System.Int64;
#else
using size_t = System.UInt32;
using h_index_t = System.Int32;
#endif
#if H_DOUBLE
using h_number_t = System.Double;
#else
using h_number_t = System.Single;
#endif

using System;
using System.Drawing;

using HAPI;

namespace Example
{
    public class BAP
    {
        protected Domain domain;

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

            BuildNetwork();

            domain.SaveAsNet("builddomain.net");
            domain.Compile();

            PropagateEvidenceInNetwork();
        }

        //Propagate evidence in domain.
        protected void PropagateEvidenceInNetwork()
        {
            domain.Propagate(Domain.Equilibrium.H_EQUILIBRIUM_SUM, Domain.EvidenceMode.H_EVIDENCE_MODE_NORMAL);
            PrintNodeMarginals(domain);
        }

        //print node marginals.
        protected void PrintNodeMarginals(Domain d)
        {
            NodeList nlist = domain.GetNodes();

            foreach (Node n in nlist)
            {
                DiscreteChanceNode node = (DiscreteChanceNode)n;
                size_t nStates = node.GetNumberOfStates();
                Console.WriteLine(node.GetLabel());
                for (size_t i = 0; i < nStates; i++)
                {
                    Console.WriteLine("-" + node.GetStateLabel(i) + " " + node.GetBelief(i));
                }
            }
        }

        //Construct numbered discrete chance node.
        protected NumberedDCNode ConstructNDC(String label, String name, size_t n)
        {
            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;
        }

        //Build the structure.
        protected void BuildStructure(NumberedDCNode A, NumberedDCNode B, NumberedDCNode C)
        {
            C.AddParent(A);
            C.AddParent(B);

            A.SetPosition(new Point(100, 200));
            B.SetPosition(new Point(200, 200));
            C.SetPosition(new Point(150, 50));
        }

        //Expression for C
        protected void BuildExpressionForC(NumberedDCNode A, NumberedDCNode B, NumberedDCNode C)
        {
            NodeList modelNodes = new NodeList();

            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);
        }

        //Specify the prior distribution of A and B.
        protected void SpecifyDistributions(NumberedDCNode A, NumberedDCNode B)
        {
            Table table = A.GetTable();
            h_number_t[] data = table.GetData();

            data[0] = (h_number_t)0.1;
            data[1] = (h_number_t)0.2;
            data[2] = (h_number_t)0.7;
            table.SetData(data);

            table = B.GetTable();
            data = table.GetData();
            data[0] = (h_number_t)0.2;
            data[1] = (h_number_t)0.2;
            data[2] = (h_number_t)0.6;
            table.SetData(data);
        }

        //Build the Bayesian network.
        public void BuildNetwork()
        {
            domain.SetNodeSize(new Size(50, 30));

            NumberedDCNode A = ConstructNDC("A1234567890123", "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);
        }

        //Build a Bayesian network and perform a propagation of
        //evidence. Print the results.
        public static int Main()
        {
            try
            {
                new BAP();
            }
            catch (ExceptionHugin e)
            {
                Console.WriteLine(e);
                return -1;
            }
            return 0;
        }
    }
}
        
Compiling:

.NET CLR 2.0, 32-bit, single precision: csc BAP.cs /platform:x86 /r:hugincs-8.5-2.0.dll

.NET CLR 2.0, 32-bit, double precision: csc BAP.cs /platform:x86 /r:hugincs2-8.5-2.0.dll /d:H_DOUBLE

.NET CLR 2.0, 64-bit, single precision: csc BAP.cs /platform:x64 /r:hugincs-8.5-2.0-x64.dll /d:X64

.NET CLR 2.0, 64-bit, double precision: csc BAP.cs /platform:x64 /r:hugincs2-8.5-2.0-x64.dll /d:X64,H_DOUBLE

.NET CLR 4.0, 32-bit, single precision: csc BAP.cs /platform:x86 /r:hugincs-8.5-4.0.dll

.NET CLR 4.0, 32-bit, double precision: csc BAP.cs /platform:x86 /r:hugincs2-8.5-4.0.dll /d:H_DOUBLE

.NET CLR 4.0, 64-bit, single precision: csc BAP.cs /platform:x64 /r:hugincs-8.5-4.0-x64.dll /d:X64

.NET CLR 4.0, 64-bit, double precision: csc BAP.cs /platform:x64 /r:hugincs2-8.5-4.0-x64.dll /d:X64,H_DOUBLE

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.

#if X64
using size_t = System.UInt64;
using h_index_t = System.Int64;
#else
using size_t = System.UInt32;
using h_index_t = System.Int32;
#endif
#if H_DOUBLE
using h_number_t = System.Double;
#else
using h_number_t = System.Single;
#endif

using System;
using HAPI;

namespace Example
{
    public class Adapt
    {
        public Adapt(String fileName)
        {
            String netFileName = fileName + ".net";
            Domain d = new Domain(netFileName, new DefaultClassParseListener());
            String logFileName = fileName + ".log";
            d.OpenLogFile(logFileName);
            d.Compile();

            SpecifyLearningParameters(d);
            PrintLearningParameters(d);

            EnterCase(d);

            PrintCase(d);

            d.Propagate(Domain.Equilibrium.H_EQUILIBRIUM_SUM, Domain.EvidenceMode.H_EVIDENCE_MODE_NORMAL);

            d.Adapt();

            d.Initialize();

            PrintNodeMarginals(d);

            d.SaveAsNet("q.net");
        }

        private void SpecifyLearningParameters(Domain d)
        {
            NodeList nl = d.GetNodes();
            h_number_t[] data;

            foreach (DiscreteChanceNode node in nl)
            {
                Table table = node.GetExperienceTable();
                data = new h_number_t[table.GetSize()];
                for (size_t i = 0; i < table.GetSize(); i++)
                    data[i] = 1;
                table.SetData(data);
            }

            foreach (DiscreteChanceNode node in nl)
            {
                Table table = node.GetFadingTable();
                data = new h_number_t[table.GetSize()];
                for (size_t i = 0; i < table.GetSize(); i++)
                    data[i] = 1;
                table.SetData(data);
            }
        }

        private void PrintLearningParameters(Domain d)
        {
            NodeList nl = d.GetNodes();

            foreach (DiscreteChanceNode dcNode in nl)
            {
                Console.WriteLine(dcNode.GetLabel() + " (" + dcNode.GetName() + "): ");
                Console.Write("   ");
                if (dcNode.HasExperienceTable())
                {
                    Table table = dcNode.GetExperienceTable();
                    h_number_t[] data = table.GetData();
                    size_t tblSize = table.GetSize();

                    for (size_t i = 0; i < tblSize; i++)
                        Console.Write(data[i] + " ");
                    Console.WriteLine();
                }
                else
                    Console.WriteLine("No experience table");

                Console.Write("   ");
                if (dcNode.HasFadingTable())
                {
                    Table table = dcNode.GetFadingTable();
                    h_number_t[] data = table.GetData();
                    size_t tblSize = table.GetSize();

                    for (size_t i = 0; i < tblSize; i++)
                        Console.Write(data[i] + " ");
                    Console.WriteLine();
                }
                else
                    Console.WriteLine("No fading table");
            }
        }

        private void EnterCase(Domain d)
        {
            NodeList nl = d.GetNodes();

            foreach (DiscreteChanceNode dcNode in nl)
            {
                dcNode.SelectState(0);
            }
            DiscreteChanceNode node = (DiscreteChanceNode)nl[1];
            node.RetractFindings();
        }

        private void PrintCase(Domain d)
        {
            NodeList nl = d.GetNodes();

            foreach (DiscreteChanceNode dcNode in nl)
            {
                Console.Write(" (" + dcNode.GetName() + ",");
                if (dcNode.EvidenceIsEntered())
                    Console.Write(" evidence is entered) ");
                else
                    Console.Write(" evidence is not entered) ");
            }
            Console.WriteLine();
        }

        private void PrintNodeMarginals(Domain d)
        {
            NodeList nl = d.GetNodes();

            foreach (DiscreteChanceNode dcNode in nl)
            {
                size_t nStates = dcNode.GetNumberOfStates();

                Console.WriteLine(dcNode.GetLabel() + " (" + dcNode.GetName() + ")");
                for (size_t i = 0; i < nStates; i++)
                    Console.WriteLine(" - " + dcNode.GetStateLabel(i) + ": " + dcNode.GetBelief(i));
            }
        }

        public static void Main(String[] args)
        {
            try
            {
                new Adapt(args[0]);
            }
            catch (ExceptionHugin eh)
            {
                Console.WriteLine(eh);
            }
        }
    }
}
        
Compiling:

.NET CLR 2.0, 32-bit, single precision: csc Adapt.cs /platform:x86 /r:hugincs-8.5-2.0.dll

.NET CLR 2.0, 32-bit, double precision: csc Adapt.cs /platform:x86 /r:hugincs2-8.5-2.0.dll /d:H_DOUBLE

.NET CLR 2.0, 64-bit, single precision: csc Adapt.cs /platform:x64 /r:hugincs-8.5-2.0-x64.dll /d:X64

.NET CLR 2.0, 64-bit, double precision: csc Adapt.cs /platform:x64 /r:hugincs2-8.5-2.0-x64.dll /d:X64,H_DOUBLE

.NET CLR 4.0, 32-bit, single precision: csc Adapt.cs /platform:x86 /r:hugincs-8.5-4.0.dll

.NET CLR 4.0, 32-bit, double precision: csc Adapt.cs /platform:x86 /r:hugincs2-8.5-4.0.dll /d:H_DOUBLE

.NET CLR 4.0, 64-bit, single precision: csc Adapt.cs /platform:x64 /r:hugincs-8.5-4.0-x64.dll /d:X64

.NET CLR 4.0, 64-bit, double precision: csc Adapt.cs /platform:x64 /r:hugincs2-8.5-4.0-x64.dll /d:X64,H_DOUBLE

Example 4: Load and inspect a network in a Windows.Form application

This example shows how HUGIN can be used in a Windows.Form application to render a graphical representation of a Domain and inspect node monitors. The example code declares a custom UserControl to render the Domain. The UserControl is then inserted on a Windows.Form. Evidence will be inserted and propagated when the user double click on any state.

Screenshot of the final application created by performing the following steps in Visual Studio 2005.

1: Create a new C# Windows Application

2: Add a reference to the HUGIN .NET api

3: Configure project properties

Configure conditional compilation symbols and platform target according to the version of the HUGIN .NET api dll referenced.

4: Create a new class called HuginControl. Insert example code.

#if X64
using size_t = System.UInt64;
using h_index_t = System.Int64;
#else
using size_t = System.UInt32;
using h_index_t = System.Int32;
#endif
#if H_DOUBLE
using h_number_t = System.Double;
#else
using h_number_t = System.Single;
#endif


using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using HAPI;

namespace Example
{
    public class HuginControl : System.Windows.Forms.Panel
    {
        private Domain domain = null;

        public HuginControl()
        {
            BackColor = Color.White;
            BorderStyle = BorderStyle.Fixed3D;
            AutoScroll = true;
        }


        public void GetDomainFromFile(String path)
        {
            try
            {
                Domain dom = new Domain(path, new DefaultClassParseListener());
                Controls.Clear();
                if (domain != null && domain.IsAlive() == true)
                    domain.Delete();
                domain = dom;
                domain.Compile();

                //populate nodes
                NodeList list = dom.GetNodes();
                foreach (Node node in list)
                {
                    if (node is DiscreteChanceNode)
                    {
                        Controls.Add(new VisibleDCNode((DiscreteChanceNode)node));
                    }
                    else
                    {
                        Controls.Add(new VisibleNode(node));
                    }
                }
                Refresh();
            }
            catch (ExceptionHugin eh)
            {
                MessageBox.Show(eh.Message);
            }
        }

        protected override void OnPaintBackground(PaintEventArgs e)
        {
            try
            {
                base.OnPaintBackground(e);
                if (domain != null && domain.IsAlive())
                {
                    NodeList list = domain.GetNodes();
                    foreach (Node node in list)
                        foreach (Node n in node.GetChildren())
                        {
                            //draw link
                            Point locationOffset = new Point(domain.GetNodeSize().Width / 2, 
                                                              domain.GetNodeSize().Height / 2);
                            Point from = node.GetPosition();
                            from.Offset(AutoScrollPosition);
                            from.Offset(locationOffset);
                            Point to = n.GetPosition();
                            to.Offset(AutoScrollPosition);
                            to.Offset(locationOffset);
                            e.Graphics.DrawLine(Pens.Black, from, to);

                            //draw arrow head
                            float scaleX = domain.GetNodeSize().Width / 2;
                            float scaleY = domain.GetNodeSize().Height / 2;
                            float scaleCos = (to.X - from.X) * ((float)domain.GetNodeSize().Height 
                                              / (float)domain.GetNodeSize().Width);
                            float scaleSin = to.Y - from.Y;
                            float norm1 = 1f / (float)Math.Sqrt(scaleCos * scaleCos + scaleSin * scaleSin);
                            scaleCos *= norm1;
                            scaleSin *= norm1;
                            float cos = to.X - from.X;
                            float sin = to.Y - from.Y;
                            float norm2 = 1f / (float)Math.Sqrt(cos * cos + sin * sin);
                            cos *= norm2;
                            sin *= norm2;
                            PointF[] arrow = {new PointF( to.X - scaleCos * scaleX-cos*20 - sin*10,
                                                          to.Y - scaleSin * scaleY - sin*20 + cos*10),
                                new PointF(to.X - scaleCos * scaleX, to.Y - scaleSin * scaleY),
                                new PointF( to.X - scaleCos * scaleX-cos*20 + sin*10, 
                                            to.Y - scaleSin * scaleY - sin*20 - cos*10),
                            };
                            e.Graphics.DrawLines(Pens.Black, arrow);
                        }
                }
            }
            catch (ExceptionHugin eh)
            {
                MessageBox.Show(eh.Message);
            }
        }

        public class VisibleDCNode : System.Windows.Forms.UserControl
        {
            private DCMonitor monitor;
            private DiscreteChanceNode node;

            public VisibleDCNode(DiscreteChanceNode dcNode)
            {
                node = dcNode;
                Size = node.GetHome().GetNodeSize();
                Location = node.GetPosition(); 
                monitor = new DCMonitor(node);
                MouseClick += MouseClickHandler;
            }

            private void MouseClickHandler(object sender, System.Windows.Forms.MouseEventArgs e)
            {
                if (!Parent.Controls.Contains(monitor))
                {
                    monitor.Location = Location;
                    Parent.Controls.Add(monitor);
                    monitor.BringToFront();
                }
                else if (Parent.Controls.Contains(monitor))
                    Parent.Controls.Remove(monitor);
            }

            protected override CreateParams CreateParams
            {
                get
                {
                    CreateParams cp = base.CreateParams;
                    cp.ExStyle |= 0x20;
                    return cp;
                }
            }

            protected override void OnPaintBackground(PaintEventArgs e)
            {
                //empty
            }

            protected override void OnPaint(PaintEventArgs pevent)
            {
                pevent.Graphics.FillEllipse(Brushes.LightYellow, 0, 0, Width - 1, Height - 1);
                pevent.Graphics.DrawEllipse(Pens.Black, 0, 0, Width - 1, Height - 1);
                pevent.Graphics.DrawString(node.GetName() + ": " + node.GetLabel(), 
                      new Font("Microsoft Sans Serif", 8.25f), Brushes.Black, 10f, Height / 2 - 5);
            }
        }

        public class DCMonitor : System.Windows.Forms.FlowLayoutPanel
        {
            private DiscreteChanceNode node;
            private Point clickLocation = new Point(0, 0);

            public DCMonitor(DiscreteChanceNode dcNode)
            {
                node = dcNode;
                MouseMove += MouseMoveHandler;
                MouseClick += MouseClickHandler;
                BackColor = Color.White;
                BorderStyle = BorderStyle.FixedSingle;
                AutoScroll = true;
                FlowDirection = FlowDirection.TopDown;
                WrapContents = false;
                Width = 180;
                Height = 150;
                Label lbl = new Label();
                lbl.Text = node.GetName() + ": " + node.GetLabel();
                lbl.MouseClick += MouseClickHandler;
                lbl.MouseMove += MouseMoveHandler;
                Controls.Add(lbl);
                for (size_t i = 0; i < node.GetNumberOfStates(); i++)
                {
                    Controls.Add(new State(node, i));
                }
            }

            private void MouseClickHandler(object sender, System.Windows.Forms.MouseEventArgs e)
            {
                BringToFront();
            }

            private void MouseMoveHandler(object sender, System.Windows.Forms.MouseEventArgs e)
            {
                if (e.Button == MouseButtons.None)
                    clickLocation = new Point(0, 0);

                if (e.Button == MouseButtons.Left && clickLocation.X != 0)
                {
                    int movementX = e.Location.X - clickLocation.X;
                    int movementY = e.Location.Y - clickLocation.Y;
                    Location = new Point(Location.X + movementX, Location.Y + movementY);
                }

                if (e.Button == MouseButtons.Left && clickLocation.X == 0)
                {
                    clickLocation = e.Location;
                }
            }

            private void RedrawNet()
            {
                Parent.Refresh();
            }

            private class State : System.Windows.Forms.UserControl
            {
                size_t stateNumber = 0;
                DiscreteChanceNode node;

                public State(DiscreteChanceNode dcNode, size_t state)
                {
                    Width = 140;
                    Height = 20;
                    BackColor = Color.White;
                    stateNumber = state;
                    node = dcNode;
                    MouseDoubleClick += MouseDoubleClickHandler;

                }

                private void MouseDoubleClickHandler(object sender, System.Windows.Forms.MouseEventArgs e)
                {
                    try
                    {
                        if (node.EvidenceIsEntered() && node.GetBelief(stateNumber) > 0)
                            node.RetractFindings();
                        else
                            node.SelectState(stateNumber);
                        node.GetHomeDomain().Propagate(Domain.Equilibrium.H_EQUILIBRIUM_SUM, 
                                                       Domain.EvidenceMode.H_EVIDENCE_MODE_NORMAL);
                        ((DCMonitor)Parent).RedrawNet();
                    }
                    catch (ExceptionHugin eh)
                    {
                        MessageBox.Show(eh.Message);
                    }
                }

                protected override void OnPaint(PaintEventArgs e)
                {
                    try
                    {
                        base.OnPaint(e);
                        Graphics g = e.Graphics;
                        Brush fillBrush = node.EvidenceIsEntered() ? Brushes.Red : Brushes.Green;
                        g.FillRectangle(fillBrush, 0, 0, (float)((Width - 1) * node.GetBelief(stateNumber)),
                                        Height - 1);
                        String text = node.GetStateLabel(stateNumber) + " " + node.GetBelief(stateNumber);
                        g.DrawString(text, new Font("Microsoft Sans Serif", 8.25f), 
                                      Brushes.Black, 10f, Height / 2 - 5);
                        g.DrawRectangle(Pens.Black, 0, 0, Width - 1, Height - 1);
                    }
                    catch (ExceptionHugin eh)
                    {
                        MessageBox.Show(eh.Message);
                    }
                }
            }
        }

        public class VisibleNode : System.Windows.Forms.UserControl
        {
            private Node node;

            public VisibleNode(Node theNode)
            {
                node = theNode;
                Size = node.GetHome().GetNodeSize();
                Location = node.GetPosition(); 
                MouseClick += MouseClickHandler;
            }

            private void MouseClickHandler(object sender, System.Windows.Forms.MouseEventArgs e)
            {
                MessageBox.Show(node.GetKind().ToString(), "Node Type");
            }

            protected override CreateParams CreateParams
            {
                get
                {
                    CreateParams cp = base.CreateParams;
                    cp.ExStyle |= 0x20;
                    return cp;
                }
            }

            protected override void OnPaintBackground(PaintEventArgs e)
            {
                //empty
            }

            protected override void OnPaint(PaintEventArgs pevent)
            {
                pevent.Graphics.FillEllipse(Brushes.Gray, 0, 0, Width - 1, Height - 1);
                pevent.Graphics.DrawEllipse(Pens.Black, 0, 0, Width - 1, Height - 1);
                pevent.Graphics.DrawString(node.GetName() + ": " + node.GetLabel(), 
                      new Font("Microsoft Sans Serif", 8.25f), Brushes.Black, 10f, Height / 2 - 5);
            }
        }


    }
}

5: Build solution

6: Switch to the windows forms designer and click on the Toolbox icon to display the toolbox pane.

7: Select the HuginControl and insert it on the form.

8: Select the OpenFileDialog control and insert an OpenFileDialog on the form

9: Select the Button control and insert a button on the form

10: Double click on the button and insert the following code:

        private void button1_Click(object sender, EventArgs e)
        {
            openFileDialog1.ShowDialog();
            if (openFileDialog1.FileName != "")
                huginControl1.GetDomainFromFile(openFileDialog1.FileName);
        }

The windows forms designer should now look something like this:

11: Build and run solution. Load and inspect your favorite networks, enjoy!

Example 5: Node naming scheme for oobn

When creating a runtime domain from a Class, all nodes are named by concatenating the names of the nodes in the list of source nodes (for source nodes see GetSource()) using a dot character ('.') as separator. This naming scheme makes it easy to work with the nodes in the runtime domain, as one can use the dot naming convention when calling GetNodeByName(String).

(A detailed explanation for the naming convention is given in the API Manual section 3.10 Creating a runtime domain).

Example oobn:

The node names in the runtime domain for the example oobn will look like:

B nested_2.C nested_2.B nested_1.C nested_1.B C A

Please note that dots are only allowed for node names in the runtime domains, and not in the classes themselves. This way potential name clashing is effectively avoided, when the runtime domain is constructed.

The following example loads an OOBN, writes out the names of the nodes in the classes, creates a runtime domain, and writes out the names of the nodes in the runtime domain.

#if X64
using size_t = System.UInt64;
using h_index_t = System.Int64;
#else
using size_t = System.UInt32;
using h_index_t = System.Int32;
#endif
#if H_DOUBLE
using h_number_t = System.Double;
#else
using h_number_t = System.Single;
#endif

using System;
using HAPI;

namespace Example
{
    public class NodeNaming
    {
        //The HUGIN GUI saves classes with an ".oobn" extension. The OOBNParseListener tries to load a nested class by
        //looking for the class in a file with the name className + ".oobn"
        public class OOBNParseListener : ClassParseListener
        {
            public void ParseError(size_t position, String msg)
            {
                Console.Error.WriteLine("Parse error location: " + position + " : " + msg);
            }

            public void InsertClass(String className, ClassCollection cc)
            {
                try
                {
                    cc.ParseClasses(className + ".oobn", this);
                }
                catch (ExceptionHugin e)
                {
                    Console.Error.WriteLine("Parsing failed: " + e.Message);
                }
            }
        }

        //Print the name of each node in the list.
        public static void PrintNodes(NodeList list)
        {
            foreach (Node n in list)
            {
                Console.Write(n.GetName() + " ");
            }
            Console.WriteLine();
        }


        //Load a hugin Object Oriented Bayesian Network, unfold classes to a domain and print out the node names.
        public static int Main(String[] args)
        {
            if (args.Length < 1)
            {
                Console.WriteLine("Usage: NodeNaming <classname>");
                Console.WriteLine("(the class <classname> is located in the file \"<classname>.oobn\")");
                return -1;
            }
            try
            {
                String className = args[0];
                ClassCollection cc = new ClassCollection();
                Console.WriteLine("Parsing classes..");
                cc.ParseClasses(className + ".oobn", new OOBNParseListener());
                Class myClass = cc.GetClassByName(className);
                if (myClass == null)
                {
                    Console.Error.WriteLine("Class is NULL");
                    return -1;
                }
                else
                {
                    foreach (Class c in cc.GetMembers()) {
                        Console.WriteLine("Nodes in class: " + c.GetName());
                        PrintNodes(c.GetNodes());
                    }
                    Console.WriteLine("\nNodes in runtime domain:");
                    Domain dom = myClass.CreateDomain();
                    PrintNodes(dom.GetNodes());
                    dom.Delete();
                    cc.Delete();
                }
            }
            catch (ExceptionHugin e)
            {
                Console.WriteLine(e);
                return -1;
            }
            return 0;
        }
    }
}          
        
Compiling:

.NET CLR 2.0, 32-bit, single precision: csc NodeNaming.cs /platform:x86 /r:hugincs-8.5-2.0.dll

.NET CLR 2.0, 32-bit, double precision: csc NodeNaming.cs /platform:x86 /r:hugincs2-8.5-2.0.dll /d:H_DOUBLE

.NET CLR 2.0, 64-bit, single precision: csc NodeNaming.cs /platform:x64 /r:hugincs-8.5-2.0-x64.dll /d:X64

.NET CLR 2.0, 64-bit, double precision: csc NodeNaming.cs /platform:x64 /r:hugincs2-8.5-2.0-x64.dll /d:X64,H_DOUBLE

.NET CLR 4.0, 32-bit, single precision: csc NodeNaming.cs /platform:x86 /r:hugincs-8.5-4.0.dll

.NET CLR 4.0, 32-bit, double precision: csc NodeNaming.cs /platform:x86 /r:hugincs2-8.5-4.0.dll /d:H_DOUBLE

.NET CLR 4.0, 64-bit, single precision: csc NodeNaming.cs /platform:x64 /r:hugincs-8.5-4.0-x64.dll /d:X64

.NET CLR 4.0, 64-bit, double precision: csc NodeNaming.cs /platform:x64 /r:hugincs2-8.5-4.0-x64.dll /d:X64,H_DOUBLE

Example 6: Sample C# Visual Studio project

Files for a sample C# project can be found in this ZIP archive cs_sample.zip. To get up and running using this project sample, perform the following steps:

  • Unzip archive containing sample project files and open solution.
  • Add reference to HUGIN .NET API: click Project>Add Reference. A dialog appears, click the 'Browse' tab and select one of the the HUGIN .NET API DLL files (located in HUGIN-install-dir\HDE8.5CS\Lib\).
  • Build and run solution!

Note:

The HUGIN .NET API DLL-file is automatically copied to the output folder. When distributing your application, remember to include the API DLL file.

Example 7: Sample Visual Basic .NET Visual Studio project

Files for a sample Visual Basic .NET project can be found in this ZIP archive vb_sample.zip. To get up and running using this project sample, perform the following steps:

  • Unzip archive containing sample project files and open solution.
  • Add reference to HUGIN .NET API: click Project>Add Reference. A dialog appears, click the 'Browse' tab and select one of the the HUGIN .NET API DLL files (located in HUGIN-install-dir\HDE8.5CS\Lib\).
  • Build and run solution!

Note:

The HUGIN .NET API DLL-file is automatically copied to the output folder. When distributing your application, remember to include the API DLL file.