SMILE Wrappers
Programmer's Manual

Version 2.2.4.R1, Built on 4/27/2024
BayesFusion, LLC

This page is intentionally left blank.

3

Table of Contents
1.

Introduction

7

2.

Licensing

93.

What's new in SMILE 2

11

4.

Platforms and Wrappers

13

4.1
Java and jSMILE ......................................................................................................... 14
4.1.1
Maven ......................................................................................................................... 15
4.2
Python and PySMILE .................................................................................................. 17
4.3

R and rSMILE ............................................................................................................. 18

4.4.NET and SMILE.NET ................................................................................................... 18

5.

Hello, SMILE Wrapper!

21

5.1

Success/Forecast model ............................................................................................. 22

5.2

VentureBN.xdsl .......................................................................................................... 22

5.3
The program .............................................................................................................. 23
5.3.1
Hello.java ..................................................................................................................... 24
5.3.2
Hello.py ....................................................................................................................... 24
5.3.3
Hello.R ......................................................................................................................... 25
5.3.4
Hello.cs ........................................................................................................................ 25

6.

Using SMILE Wrappers

6.1

27

Error handling ............................................................................................................ 28

6.2
Networks, nodes and arcs .......................................................................................... 28
6.2.1
Network ...................................................................................................................... 28
6.2.2
Nodes .......................................................................................................................... 29
6.2.3
Arcs .............................................................................................................................. 30
6.3
Anatomy of a node .................................................................................................... 31
6.3.1
Node definition ........................................................................................................... 32
6.3.2
Node value .................................................................................................................. 32
6.3.3
Node evidence ............................................................................................................ 33
6.3.4
Other node attributes ................................................................................................. 35
6.4
Multidimensional arrays ............................................................................................ 36
6.5

Input and Output ....................................................................................................... 36

6.6

Inference ................................................................................................................... 37

6.7

User properties .......................................................................................................... 38

6.8

Submodels ................................................................................................................. 39

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

4

Table of Contents
6.9
Discrete nodes and numeric domains ......................................................................... 40
6.9.1
Outcome intervals ....................................................................................................... 41
6.9.2
Outcome point values ................................................................................................. 42
6.10
Canonical nodes ......................................................................................................... 42
6.10.1
Noisy-MAX ................................................................................................................... 43
6.10.2
Noisy-Adder ................................................................................................................ 45
6.11
Influence diagrams .................................................................................................... 45
6.12
Dynamic Bayesian networks ...................................................................................... 45
6.12.1
Unrolling ...................................................................................................................... 46
6.12.2
Temporal definitions ................................................................................................... 48
6.12.3
Temporal evidence ..................................................................................................... 48
6.12.4
Temporal beliefs ......................................................................................................... 49
6.13
Continuous models .................................................................................................... 49
6.13.1Equation-based nodes ................................................................................................ 49
6.13.2
Continuous inference .................................................................................................. 50
6.14
Hybrid models ........................................................................................................... 51
6.15
Equations reference ................................................................................................... 52
6.15.1
Operators .................................................................................................................... 52
6.15.2
Random Number Generators ..................................................................................... 54
6.15.3
Statistical Functions .................................................................................................... 64
6.15.4
Arithmetic Functions ................................................................................................... 65
6.15.5
Combinatoric Functions .............................................................................................. 66
6.15.6
Trigonometric Functions ............................................................................................. 67
6.15.7Hyperbolic Functions .................................................................................................. 67
6.15.8Logical/Conditional functions ..................................................................................... 68
6.15.9Custom Functions ....................................................................................................... 69
6.16
Diagnosis ................................................................................................................... 70
6.16.1
Diagnostic roles ........................................................................................................... 70
6.16.2
Observation cost ......................................................................................................... 71
6.16.3
Diagnostic session ....................................................................................................... 71
6.16.4
Distance and entropy-based measures ...................................................................... 72
6.17
Learning .................................................................................................................... 75
6.17.1
Learning network structure ........................................................................................ 75
6.17.2
Learning network parameters .................................................................................... 77
6.17.3
Validation .................................................................................................................... 78

7.

Tutorials

81

7.1
Tutorial 1: Creating a Bayesian Network .................................................................... 82
7.1.1
Tutorial1.java .............................................................................................................. 85

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

5

Table of Contents
7.1.2
7.1.3
7.1.4
7.2
7.2.1
7.2.2
7.2.3
7.2.4
7.3
7.3.1
7.3.2
7.3.3
7.3.4
7.4
7.4.1
7.4.2
7.4.3
7.4.4
7.5
7.5.1
7.5.2
7.5.3
7.5.4
7.6
7.6.1
7.6.2
7.6.3
7.6.4
7.7
7.7.1
7.7.2
7.7.3
7.7.4
7.8
7.8.1
7.8.2
7.8.3
7.8.4
7.9
7.9.1

SMILE Wrappers

Tutorial1.py ................................................................................................................. 87
Tutorial1.R ................................................................................................................... 89
Tutorial1.cs .................................................................................................................. 91
Tutorial 2: Inference with a Bayesian Network ........................................................... 93
Tutorial2.java .............................................................................................................. 94
Tutorial2.py ................................................................................................................. 95
Tutorial2.R ................................................................................................................... 96
Tutorial2.cs .................................................................................................................. 98
Tutorial 3: Exploring the contents of a model ............................................................. 99
Tutorial3.java ............................................................................................................ 102
Tutorial3.py ............................................................................................................... 104
Tutorial3.R ................................................................................................................. 105
Tutorial3.cs ................................................................................................................ 106
Tutorial 4: Creating the Influence Diagram ............................................................... 108
Tutorial4.java ............................................................................................................ 110
Tutorial4.py ............................................................................................................... 111
Tutorial4.R ................................................................................................................. 112
Tutorial4.cs ................................................................................................................ 113
Tutorial 5: Inference in an Influence Diagram ........................................................... 114
Tutorial5.java ............................................................................................................ 116
Tutorial5.py ............................................................................................................... 117
Tutorial5.R ................................................................................................................. 118
Tutorial5.cs ................................................................................................................ 120
Tutorial 6: Dynamic model ....................................................................................... 122
Tutorial6.java ............................................................................................................ 124
Tutorial6.py ............................................................................................................... 127
Tutorial6.R ................................................................................................................. 128
Tutorial6.cs ................................................................................................................ 130
Tutorial 7: Continuous model ................................................................................... 133
Tutorial7.java ............................................................................................................ 134
Tutorial7.py ............................................................................................................... 137
Tutorial7.R ................................................................................................................. 138
Tutorial7.cs ................................................................................................................ 140
Tutorial 8: Hybrid model .......................................................................................... 143
Tutorial8.java ............................................................................................................ 144
Tutorial8.py ............................................................................................................... 146
Tutorial8.R ................................................................................................................. 148
Tutorial8.cs ................................................................................................................ 150
Tutorial 9: Diagnosis ................................................................................................ 153
Tutorial9.java ............................................................................................................ 159

Version 2.2.4.R1, Built on 4/27/2024

6

Table of Contents
7.9.2
Tutorial9.py ............................................................................................................... 162
7.9.3
Tutorial9.R ................................................................................................................. 164
7.9.4
Tutorial9.cs ................................................................................................................ 166
7.10
Tutorial 10: Structure learning ................................................................................. 169
7.10.1
Tutorial10.java .......................................................................................................... 174
7.10.2
Tutorial10.py ............................................................................................................. 177
7.10.3
Tutorial10.R ............................................................................................................... 179
7.10.4
Tutorial10.cs .............................................................................................................. 180

8.

Acknowledgments

Index

SMILE Wrappers

185
0

Version 2.2.4.R1, Built on 4/27/2024

Introduction

8

Introduction
1

Introduction

Welcome to SMILE Wrapper Programmer's Manual, Version 2.2.4.R1, Built on 4/27/2024.
For the most recent version of this manual, please visit https://support.bayesfusion.com/docs/.
To download the software described in this manual, go to http://download.bayesfusion.com.
SMILE is a C++ software library for performing Bayesian inference. In order to ensure that its functionality canbe easily integrated into software written in other languages, BayesFusion, LLC, provides wrapper libraries for
Java, Python, R and .NET. The names of these products are:· jSMILE (Java and environments which can instantiate and use the JVM)
· PySMILE (Python 2.7 and 3.x)
· rSMILE (R 4.x)
· SMILE.NET (.NET)

Each wrapper includes SMILE, so you do not need to download SMILE or know how to use C++ SMILE in order
to use the wrappers. We strive to ensure feature symmetry between the wrappers - features available in one
wrapper are generally available in other wrappers.
We have also developed SMILE.COM, a wrapper exposing SMILE functionality through Windows' COM(Common Object Model). The target audience for SMILE.COM are Microsoft Excel users, although the librarywill work with any environment extensible through COM. This manual does not contain documentation for
SMILE.COM.
If you are new to SMILE and would like to begin with an informal, tutorial-like introduction, please start with
the Java, Python or .NET section of Platforms and Wrappers 14 (depending on the programming language
you're going to use), followed by Hello SMILE Wrapper! 22 section. If you are an advanced user, please browse
through the Table of Contents or search for the topic of your interest.
This manual refers to a good number of concepts that are assumed to be known to the reader, such asprobability, utility, decision theory and decision analysis, Bayesian networks, influence diagrams, etc. Should
you want to learn more about these, please refer to GeNIe manual. SMILE is GeNIe's Application Programmer's
Interface (API) and practically every elementary operation performed with GeNIe translates to calls to SMILE
methods. Being familiar with GeNIe may prove extremely useful in learning SMILE. Understanding some of
SMILE’s functionality may be easier when performed interactively in GeNIe. GeNIe manual, along with all other
BayesFusion documentation, is available at https://support.bayesfusion.com/docs. Other resources, including
introduction to probabilistic graphical models, are available at https://www.bayesfusion.com/resources/.
Yet another useful resource is available at https://repo.bayesfusion.com. The site is powered by BayesBox, ourinteractive model viewer/repository software. The repository contains more than 100 example Bayesian
networks, hybrid Bayesian networks, dynamic Bayesian networks, and influence diagrams. BayesBox runs
SMILE on the server side and calls into its API to calculate the posterior probabilities after evidence is modified
in the web browser.

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

Licensing

10

Licensing2

Licensing

SMILE wrappers are commercial products that require a development license to use. There are two types ofdevelopment licenses: Academic and Commercial. Academic license is free of charge for research and teachinguse by those users affiliated with an academic institution. All other use requires a commercial license, available
for purchase from BayesFusion, LLC.
Deployment of SMILE library or SMILE wrappers, i.e., embedding them into user programs, requires a
deployment license. There are two types of deployment licenses: Server license, which allows a program linked
with SMILE to be deployed on a computer server, and end-user program license, which allows distribution ofuser programs that include SMILE. Please contact BayesFusion, LLC, for details of the licenses and pricing.
The licensing system is implemented as a fragment of code which contains your license key and must be
executed as first call into SMILE wrapper that your program uses. This code is not included in the SMILE
wrapper binary distribution, it is personalized by BayesFusion, LLC, for you or your organization. The
license keys are provided in a compressed .zip file, which contains Java, R, Python, C# and VB.NET code. 6month academic and free 30-day evaluation licenses can be obtained directly at
https://download.bayesfusion.com.

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

What's new in SMILE 2

12What's new in SMILE 2
3

What's new in SMILE 2

Major new features in SMILE 2 are:· Support for metalog distributions in continuous nodes. There is no new API for metalog distributions,

equation nodes can use Metalog and MetalogA functions in their equations. For details, see the metalog entry
in the Random Number Generators 54 section.
· Discrete nodes with outcomes based on numeric intervals or point values. See the Discrete nodes and numericdomains 40 section for details.

Version 2.2 introduces new diagnostic algorithms for single-fault and multi-fault diagnosis 70 .

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

Platforms and Wrappers

14

Platforms and Wrappers
4

Platforms and Wrappers

In addition to ensuring feature parity between wrappers, we designed them to use similar names for the classes
and methods. For example, all three wrappers contain the Network class. At the same time, wrappers honor theidioms of the specific languages they target:· Network class in jSMILE has addNode method· Network class in PySMILE has add_node method· Network class in rSMILE has addNode method· Network class in SMILE.NET has AddNode method

As you can see, the naming choices for the methods reflect the naming conventions of different programming
languages.
The remainder of this chapter describes the platform-specific details related to the use of SMILE Wrappers.

4.1

Java and jSMILEjSMILE is compatible with JDK 8 and newer. See the Maven 15 section below for information about using
jSMILE in a Maven-based project, including the location of BayesFusion's Maven repository and POM
integration with jSMILE's native library.
jSMILE contains two parts: the jar file and the native library, the latter is specific to your operating system. The
jar file, named jsmile-x.y.z.jar where x.y.z is the version number, can be used as any Java jar.
The native library is used by the code in the jar file through Java Native Interface (JNI). The name of the native
library file is platform-dependent:· jsmile.dll on Windows· jsmile.so on Linux· jsmile.jnilib on macOSIn the static initializer of the smile.Wrapper class, jSMILE will attempt to load its native library. If thejsmile.native.library system property is set, jSMILE calls System.load method, which requires a complete
path name of the native library (no relative paths are allowed), using the value of the property as the path name.
Otherwise, the System.loadLibrary method is used. In such case, the native library must be located in one ofthe directories specified by the java.library.path property. Note that java.library.path cannot be set
once JVM is running, but jsmile.native.library can be set with a call to System.setProperty.
Your license key should be pasted into the source code of your program.
The classes defined in jSMILE are located in smile and smile.learning packages. There are two types ofclasses:· simple objects with public data members· wrappers for C++ objects from SMILE library

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

15

Platforms and Wrappers
The second group of classes, which includes smile.Network, is derived from smile.Wrapper. Each object
instance of these classes has a related C++ object, which is deallocated by the Java object's finalizer or by a callto Wrapper.Dispose method. To ensure early deallocation of C++ object resources associated with your Java
object, Dispose should be called as soon as the program doesn't need the object anymore. If Dispose is notcalled, the deallocation will happen in the finalizer during garbage collection.

4.1.1

Maven

If your Java project uses Maven, you can use BayesFusion's Maven repository to integrate jSMILE with yourproject. The repository URL is https://support.bayesfusion.com/maven-X, where X is either A for jSMILE
Academic, or B for the commercial version of the library. The artifact id is jsmile and the group id iscom.bayesfusion, regardless of the repository used.
In addition to the jsmile artifact, the repository contains three separate .zip artifacts for three platform-specificnative libraries:· jsmile-native-win64 for 64-bit Windows· jsmile-native-linux64 for 64-bit Linux· jsmile-native-macos for macOS

Please do not add the references to these artifacts in the <dependencies> section of the POM file. Instead, use
the maven-dependency-plugin to integrate these with your Maven-based build process and ensure that thenative library is extracted from the artifact's .zip file into the project directory during the compile phase. For
projects that only run on one of the three supported native platforms, the artifact id specified in the plugin
configuration can refer directly to one of three artifacts listed above. Another option is to use Maven's OS-based
profile activation to select the appropriate native library. The POM file below uses this approach. Note that the
CPU architecture is not tested against (we assume 64-bit environment for Windows and Linux). The location of
the extracted library is arbitrarily set to ${project.basedir}. jSMILE version referenced in the POM is set to 2.2.0.
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.bayesfusion</groupId><artifactId>test-maven-repo</artifactId><version>0.0.1-SNAPSHOT</version><repositories><repository><id>bayesfusion-repo</id><name>BayesFusion Repository</name><url>https://support.bayesfusion.com/maven-B</url></repository></repositories><dependencies><dependency><groupId>com.bayesfusion</groupId><artifactId>jsmile</artifactId><version>2.2.0</version></dependency></dependencies>

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

Platforms and Wrappers

16<profiles><profile><id>MacNative</id><activation><os><family>mac</family></os></activation><properties><nativeSuffix>macos</nativeSuffix></properties></profile><profile><id>WinNative</id><activation><os><family>Windows</family></os></activation><properties><nativeSuffix>win64</nativeSuffix></properties></profile><profile><id>LinuxNative</id><activation><os><family>Unix</family></os></activation><properties><nativeSuffix>linux64</nativeSuffix></properties></profile></profiles><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-dependency-plugin</artifactId><executions><execution><id>resource-dependencies</id><phase>compile</phase><goals><goal>unpack</goal></goals><configuration><artifactItems><artifactItem><groupId>com.bayesfusion</groupId><artifactId>jsmile-native-${nativeSuffix}</artifactId><version>2.2.0</version>

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

17

Platforms and Wrappers<type>zip</type><overWrite>true</overWrite><includes>*jsmile*</includes></artifactItem></artifactItems><outputDirectory>${project.basedir}</outputDirectory></configuration></execution></executions></plugin></plugins></build></project>

4.2

Python and PySMILE

To use PySMILE in your Python program, you just need to import the pysmile module:
import pysmileYour license key can be pasted directly into your Python code, or imported:
import pysmile_license

PySMILE binaries are available for Python 2.7 and Python 3.x. PySMILE consists of a single dynamically loaded
library. The library name is platform-dependent:· pysmile.pyd on Windows· pysmile.so on Linux and macOSYou can manually download PySMILE binaries from BayesFusion's software download website, or use pip(Python package manager) to automate the download. BayesFusion's software download website contains
PySMILE binaries compatible with the single recent stable Python version, but our pip repository hosts builds
for multiple Python releases.
To download using pip:
python -m pip install --no-cache-dir --index-url https://support.bayesfusion.com/pysmile-B/ pysmile

Replace pysmile-B with pysmile-A in the repository URL if you're using PySMILE academic. To force redownload of the pysmile module from the BayesFusion repository, add --force-reinstall to the commandline.
Sonatype Nexus requires the pip repository URLs to end with simple. We maintain pip repository mirrors forusers of Nexus at:https://support.bayesfusion.com/python/pysmile-A/simple/
https://support.bayesfusion.com/python/pysmile-B/simple/

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

18

Platforms and Wrappers
4.3

R and rSMILErSMILE is distributed as a compiled R package. rSMILE binaries are available for R 3.x.
To install rSMILE, use install.packages:
install.packages(<path-to-local-copy-of-rSMILE-package>)Once the package is installed, use the library function to enable rSMILE functionality:
library(rSMILE)
Your license key can be pasted directly into your R code, or read from the license file:source("License.R")

From December 2019 onwards, the licensing keys for R are generated in the format compatible with rSMILE andare ready to be used with rSMILE. If you obtained your SMILE licensing key before December 2019, yourLicense.R file uses the syntax for jSMILE and rJava. To modify your licensing key, replace.jnew("smile.License",paste(
withrSMILE::License(paste(
Another required modification applies to the numeric part of the licensing key: replace .jbyte withas.integer. Please contact BayesFusion if you encounter any problems with the modification, or, if you're an
academic user, generate new licensing key at our academic downloads site.

4.4.NET and SMILE.NET

SMILE.NET, which is compatible with .NET framework 4.x, consists of a single mixed-mode assembly named
smilenet.dll. To use the library, simply add smilenet.dll as a reference in your project.
We build SMILE.NET using C++/CLI compiler in Visual Studio 2013 to create a seamless package containing
both native code (the C++ SMILE library) and .NET wrapper code. This compiler enforces the use of the
dynamic C++ runtime library. This means that smilenet.dll has a runtime dependency on VS2013 C++
runtime. Your Windows system is likely to have this component already installed. However, if you are getting
error messages referring to missing DLLs like MSVCP120.DLL or MSVCR120.DLL, you need to download andinstall the "Visual C++ Redistributable Package for Visual Studio 2013" from Microsoft's website.
Your license key should be pasted into the sources of your program. We provide the keys as C# and VB.NET
code.
The classes defined in SMILE.NET are located in Smile and Smile.Learning namespaces. There are two typesof classes:· simple objects with public data members· wrappers for C++ objects from SMILE library

The second group of classes, which includes Smile.Network, is derived from Smile.WrappedObject and also
implements .NET's IDisposable interface. Each object instance of these classes has a related C++ object, which
is deallocated by the .NET object's finalizer or its IDisposable::Dispose method. You can use C#'s using

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

19

Platforms and Wrappers
statement to ensure deterministic finalization, otherwise the deallocation will be performed in the finalizer
during garbage collection.
using (Network net = new Network()){// use the net object in this scope}

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

This page is intentionally left blank.

Hello, SMILE Wrapper!

22

Hello, SMILE Wrapper!
5

Hello, SMILE Wrapper!

In this section, we will show how SMILE can load and use a model created in GeNIe to perform useful work. We
will use the model developed in the GeNIe on-line help (Section Hello GeNIe!). The model for this problem isavailable as one of the example networks (file VentureBN.xdsl). If you have GeNIe installed, you can copy thefile into your working directory. The same file is also included in the zip file containing all source code fortutorials, available from http://support.bayesfusion.com/docs.
Alternatively, create a file named VentureBN.xdsl with any text editor by copying the content of the
VentureBN.xdsl 22 section below.

5.1

Success/Forecast model

The model encodes information pertaining to a problem faced by a venture capitalist, who considers a risky
investment in a startup company. A major source of uncertainty about her investment is the success of thecompany. She is aware of the fact that only around 20% of all start-up companies succeed. She can reduce thisuncertainty somewhat by asking expert opinion. Her expert, however, is not perfect in his forecasts. Of all startup companies that eventually succeed, he judges about 40% to be good prospects, 40% to be moderate
prospects, and 20% to be poor prospects. Of all start-up companies that eventually fail, he judges about 10% to
be good prospects, 30% to be moderate prospects, and 60% to be poor prospects.

5.2

VentureBN.xdsl<?xml version="1.0" encoding="ISO-8859-1"?><smile version="1.0" id="VentureBN" numsamples="1000"><nodes><cpt id="Success"><state id="Success" /><state id="Failure" /><probabilities>0.2 0.8</probabilities></cpt><cpt id="Forecast"><state id="Good" /><state id="Moderate" /><state id="Poor" /><parents>Success</parents><probabilities>
0.4 0.4 0.2 0.1 0.3 0.6</probabilities></cpt>

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

23

Hello, SMILE Wrapper!
</nodes><extensions><genie version="1.0" app="GeNIe 2.1.1104.2"
name="VentureBN"
faultnameformat="nodestate"><node id="Success"><name>Success of the venture</name><interior color="e5f6f7" /><outline color="0000bb" /><font color="000000" name="Arial" size="8" /><position>54 11 138 62</position></node><node id="Forecast"><name>Expert forecast</name><interior color="e5f6f7" /><outline color="0000bb" /><font color="000000" name="Arial" size="8" /><position>63 105 130 155</position></node></genie></extensions></smile>5.3

The program

We will show how to load this model using SMILE, how to enter observations (evidence), how to perform
inference, and how to retrieve the results of SMILE’s calculations. Three complete source code versions (Java,
Python, R, C#) are included below. Note that you'll need to use your SMILE license key. See the Licensing 10
section of this manual if you want to obtain your academic or trial license key. See Platforms and Wrappers 14
section for language-specific information on licensing.
Our network object - an instance of Network class - is declared as local variable. We proceed to read the XDSL
file from disk:Java:
net.readFile("VentureBN.xdsl");Python: net.read_file("VentureBN.xdsl")
R:
net$readFile("VentureBN.xdsl")
C#:
net.ReadFile("VentureBN.xdsl");
We want to set the evidence on the Forecast node to Moderate. The wrappers can use node and outcome
identifiers directly:Java:
net.setEvidence("Forecast", "Moderate");Python: net.set_evidence("Forecast", "Moderate")R:
net$setEvidence("Forecast", "Moderate")
C#:
net.SetEvidence("Forecast", "Moderate");Now we can update the network:Java:
net.updateBeliefs();
Python: net.update_beliefs()
R:
net$updateBeliefs()
C#:
net.UpdateBeliefs();
After network update we can retrieve the posterior probabilities of the Success node:Java:
double[] beliefs = net.getNodeValue("Success");
Python: beliefs = net.get_node_value("Success")

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

24

Hello, SMILE Wrapper!
R:C#:

beliefs <- net$getNodeValue("Success")double[] beliefs = net.GetNodeValue("Success");

To print the probabilities, we simply iterate over the elements of the returned numeric array. Each probability
printed to the console is preceeded by the identifier of the node outcome. If you compile and run the program,the output should be:
Success=0.25
Failure=0.75“Success” and “Failure” are outcome identifiers of the Success node, which in Java are returned by the
Network.getOutcomeId method. At this point you should easily derive the equivalent method names for
Python, R and C# from Java method name.
Note that in case of any of method calls failing, the exception would be thrown. SMILE wrappers do not use
error codes on failure.
We will build upon the simple network described in this chapter in the Tutorials 82 section of this manual.

5.3.1

Hello.java

import smile.*;public class Hello {public static void main(String[] args) {System.out.println("Insert your licensing key below.");// new smile.License(...);
Network net = new Network();net.readFile("VentureBN.xdsl");
net.setEvidence("Forecast", "Moderate");net.updateBeliefs();
double[] beliefs = net.getNodeValue("Success");for (int i = 0; i < beliefs.length; i ++) {System.out.println(net.getOutcomeId("Success", i) + " = " + beliefs[i]);
}
}
}

5.3.2

Hello.py

import pysmile# pysmile_license is your license key
import pysmile_license
def hello_smile():net = pysmile.Network()
net.read_file("VentureBN.xdsl");
net.set_evidence("Forecast", "Moderate")
net.update_beliefs()
beliefs = net.get_node_value("Success")for i in range(0, len(beliefs)):
print(net.get_outcome_id("Success", i) + "=" + str(beliefs[i]))SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

25

Hello, SMILE Wrapper!
hello_smile()

5.3.3

Hello.R

library(rSMILE)# License.R is your licensing key
source("License.R")
net <- Network()
net$readFile("VentureBN.xdsl")
net$setEvidence("Forecast", "Moderate")
net$updateBeliefs()
beliefs <- net$getNodeValue("Success")for (i in 1:length(beliefs)) {cat(sprintf("%s = %f\n", net$getOutcomeId("Success", i-1L), beliefs[i]))
}

5.3.4

Hello.cs

using System;
using Smile;namespace SmileNetTutorial{class Hello{static void Main(string[] args){
Console.WriteLine("Insert your SMILE licensing key here.");// new Smile.License(...);
Network net = new Network();
net.ReadFile("VentureBN.xdsl");
net.SetEvidence("Forecast", "Moderate");net.UpdateBeliefs();
double[] beliefs = net.GetNodeValue("Success");for (int i = 0; i < beliefs.Length; i++){Console.WriteLine("{0} = {1}", net.GetOutcomeId("Success", i), beliefs[i]);}}}}SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

This page is intentionally left blank.

Using SMILE Wrappers

28

Using SMILE Wrappers
6

Using SMILE Wrappers

6.1

Error handling

SMILE Wrappers throw exceptions when the underlying C++ SMILE calls fail. The exception types are:Java:
smile.SMILEException
Python: pysmile.SMILEExcetpion
C#:
Smile.SmileException

6.2

Networks, nodes and arcs

The most important class defined by SMILE Wrappers is Network. The objects of this class act as containers fornodes and are responsible for node creation and destruction. Nodes and arcs are always created and destroyed
by invoking the methods of the Network class. The access to existing nodes and arcs is always performed
through the Network object where they live.
There are no classes representing nodes and arcs. The Network object works as a facade for the complex
underlying data structure.

6.2.1

Network

To create a network, simply use a default constructor:Java:
import smile.*;...
Network net = new Network();
Python:import pysmile...
net = pysmile.Network()R:library(rSMILE)...
net <- Network()C#:using Smile;...
Network net = new Network();
See the Platforms and Wrappers 14 section for the platform-specific information about the lifetime of instancesof Network class.

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

29

Using SMILE Wrappers
6.2.2

Nodes

Use the following methods to add and delete nodes:
Java:
Network.addNode
Network.deleteNode
Python:Network.add_node
Network.delete_node
R:
Network$addNode
Network$deleteNode
C#:
Network.AddNode
Network.DeleteNode
When creating the node you need to specify its type.
Within the network, the nodes are uniquely identified by their handle. The node handle is a non-negative
integer, which is preserved when network is copied. In addition to handles, each node has an unique (in thecontext of its containing network), persistent, textual identifier. This identifier is specified as an argument to
Network.addNode method at node creation (it may be changed later). The identifiers in SMILE start are casesensitive, start with a letter and contain letters, digits and underscores. Node’s identifier can be converted tonode handle with a call to Network.getNode method.
All methods of the Network class dealing with nodes can use either integer node handles or string
identifiers to specify the nodes. However, the versions using identifiers will be internallyperforming O(N) string lookup (where N is the number of nodes in the network), while theversions using handlers are only using O(1) validity check.
The values of the handles are not guaranteed to be consecutive or start from any particular value. To iterate overnodes in the network, use Network.getFirstNode and getNextNode:Java:
for (int h = net.getFirstNode(); h >= 0; h = net.getNextNode(h)) {// do something with net and node identified by h}Python:h = net.get_first_node()while (h >= 0):# do something with net and node identified by hh = net.get_next_node(h)R:for (h in net$getAllNodes()) {# do something with net and node identified by h}C#:for (int h = net.GetFirstNode(); h >= 0; h = net.GetNextNode(h))

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

30

Using SMILE Wrappers{// do something with net and node identified by h}Note the loop exit condition - we proceed only if the returned handle is greater than or equal to zero.
Here are the methods to obtain an array of all node handles, an array of all node identifiers or a number of nodes
in the network:
Java:
Network.getAllNodes
Network.getAllNodeIds
Network.getNodeCount
Python:Network.get_all_nodes
Network.get_all_node_ids
Network.get_node_countR:
Network$getAllNodes
Network$getAllNodeIds
Network$getNodeCountC#:
Network.GetAllNodes
Network.GetAllNodeIds
Network.NodeCount (read-only property)
Nodes may be marked as targets with Network.setTarget method. Target nodes are always guaranteed to be
updated by the inference algorithm. Other nodes, i.e., nodes that are not designated as targets, may be updated
or not, depending on the internals of the algorithm used, but are not guaranteed to be updated. Focusing
inference on the target nodes can reduce time and memory required to complete the calculation. When notargets are specified, SMILE assumes that all nodes are of interest to the user.

6.2.3

Arcs

Here are the Network class methods to add and delete arcs between nodes:
Java:
Network.addArc
Network.deleteArc
Python:Network.add_arc
Network.delete_arc
R:
Network$addArc
Network$deleteArc
C#:
Network.AddArc

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

31

Using SMILE Wrappers
Network.DeleteArc
The graph defined by nodes and arcs in a Network class instance a directed acyclic graph (DAG) at all times. An
attempt to add an arc which would create a cycle in the graph will cause an exception.
To inspect the graph structure, you can use the following methods:
Java:
Network.getParents
Network.getParentIds
Network.getChildren
Network.getChildIds
Python:Network.get_parents
Network.get_parent_ids
Network.get_children
Network.get_child_ids
R:
Network$getParents
Network$getParentIds
Network$getChildren
Network$getChildIds
C#:
Network.GetParents
Network.GetParentIds
Network.GetChildren
Network.GetChildIds
The methods with the "Ids" suffix return the arrays of string identifiers of the related nodes, otherwise the arraysof integer node handles are used.

6.3

Anatomy of a node

The different aspects of a node available through Network class methods are:· node definition· node value· node evidence· attributes which do not affect inference, but determine node's location, color, name, etc.

The definition of the node specifies how it interacts with other nodes in the network. The node definition is
written as part of the network when the network is saved to a file or serialized as a string variable. For generalchance node the definition consists of conditional probability table (CPT) and list of state names.
The value of the node contains the values (typically, the marginal probability distribution or the expectedutilities) calculated for the node by the inference algorithm. Unlike the definition, the value is not written as partof the network.

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

32

Using SMILE Wrappers

6.3.1

Node definition

The definition of the node specifies how it interacts with other nodes in the network. The node definition is
written as part of the network when the network is saved to a file or serialized.
Use the following methods to read or write the numeric part of node definition:
Java:
Network.setNodeDefinition
Network.getNodeDefinition
Python:Network.set_node_definition
Network.get_node_definition
R:
Network$setNodeDefinition
Network$getNodeDefinition
C#:
Network.SetNodeDefinition
Network.GetNodeDefinition
The discrete nodes (including the general chance nodes) also have the list of outcomes as part of their definition.
The outcomes are identified by string identifiers, which must be unique within the node and follow SMILE rules
for identifier: are case-sensitive, start with a letter and contain letters, digits and underscores. Outcomes can be
added, deleted or renamed.

6.3.2

Node value

The value of the node contains the values (typically, the marginal probability distribution or the expectedutilities) calculated for the node by the inference algorithm. Unlike the definition, the value is not written as partof the network during I/O operations. In influence diagrams the node value is calculated for multiple sets ofdecisions. In such case, you also need to retrieve the indexing parents of node value to properly interpret the
numbers. Note that value indexing parents are not the same as node parents. The value also stores its validity
status; a flag set to true by the inference algorithm after it succesfully completes the calculations for the given
node. You should not call any value-reading methods unless the value is valid.
Here are the methods related to node values:
Java:
Network.isValueValid
Network.getNodeValue
Network.getValueIndexingParents
Network.getValueIndexingParentIds
Python:Network.is_value_valid
Network.get_node_value

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

33

Using SMILE Wrappers
Network.get_value_indexing_parents
Network.get_value_indexing_parent_ids
R:
Network$isValueValid
Network$getNodeValue
Network$getValueIndexingParents
Network$getValueIndexingParentIds
C#:
Network.IsValueValid
Network.getNodeValue
Network.getValueIndexingParents
Network.getValueIndexingParentIds

6.3.3

Node evidence

The output from the inference algorithms depends on the node definitions and the evidence set on nodes in the
network. The evidence for discrete nodes is specified by outcome index or outcome identifier. For continuous,
the evidence is a number, and related methods have 'cont' in their names (for example,
Network.getContEvidence). In dynamic Bayesian networks (DBNs), evidence is specified for a specified timeslice. The evidence methods for DBNs have 'temporal' in their names (for example,
Network.getTemporalEvidence).
Nodes can become propagated evidence (implied by other evidence set in the network).
Special type of evidence for discrete nodes is virtual evidence, which is a probability distribution over theoutcomes of the node.
The methods related to evidence include:Java:
Network.isEvidence
Network.isPropagatedEvidence
Network.isRealEvidence
Network.clearEvidence
Network.getEvidence
Network.getEvidenceId
Network.setEvidence
Network.getContEvidence
Network.setContEvidence
Network.isVirtualEvidence
Network.getVirtualEvidence
Network.setVirtualEvidence
Network.isTemporalEvidence
Network.hasTemporalEvidence
Network.clearTemporalEvidence
Network.getTemporalEvidence
Network.getTemporalEvidenceId
Network.getTemporalVirtualEvidence
Network.setTemporalEvidence
Network.setTemporalVirtualEvidence

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

Using SMILE Wrappers

34

Python:Network.is_evidence
Network.is_propagated_evidence
Network.is_real_evidence
Network.clear_evidence
Network.get_evidence
Network.get_evidence_id
Network.set_evidence
Network.get_cont_evidence
Network.set_cont_evidence
Network.is_virtual_evidence
Network.get_virtual_evidence
Network.set_virtual_evidence
Network.is_temporal_evidence
Network.has_temporal_evidence
Network.clear_temporal_evidence
Network.get_temporal_evidence
Network.get_temporal_evidence_id
Network.get_temporal_virtual_evidence
Network.set_temporal_evidence
Network.set_temporal_virtual_evidenceR:
Network$isEvidence
Network$isPropagatedEvidence
Network$isRealEvidence
Network$clearEvidence
Network$getEvidence
Network$getEvidenceId
Network$setEvidence
Network$getContEvidence
Network$setContEvidence
Network$isVirtualEvidence
Network$getVirtualEvidence
Network$setVirtualEvidence
Network$isTemporalEvidence
Network$hasTemporalEvidence
Network$clearTemporalEvidence
Network$getTemporalEvidence
Network$getTemporalEvidenceId
Network$getTemporalVirtualEvidence
Network$setTemporalEvidence
Network$setTemporalVirtualEvidenceC#:
Network.IsEvidence
Network.IsPropagatedEvidence
Network.IsRealEvidence
Network.ClearEvidence
Network.GetEvidence
Network.GetEvidenceId
Network.SetEvidence
Network.GetContEvidence
Network.SetContEvidence

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

35

Using SMILE Wrappers
Network.IsVirtualEvidence
Network.GetVirtualEvidence
Network.SetVirtualEvidence
Network.IsTemporalEvidence
Network.HasTemporalEvidence
Network.ClearTemporalEvidence
Network.GetTemporalEvidence
Network.GetTemporalEvidenceId
Network.GetTemporalVirtualEvidence
Network.SetTemporalEvidence
Network.SetTemporalVirtualEvidence

6.3.4

Other node attributes

Nodes have other attributes, which are not used by inference algorithms, but are required for graphicalinterpretation of the Bayesian network. Among these are: node name, description, position, color, etc. For
brevity, the list below only includes the getter methods. The corresponding setters are also available.
Java:
Network.getNodeName
Network.getNodeDescription
Network.getNodePosition
Network.getNodeBgColor
Network.getNodeTextColor
Network.getNodeBorderColor
Python:Network.get_node_nameNetwork.get_node_description
Network.get_node_position
Network.get_node_bgColor
Network.get_node_textColor
Network.get_node_borderColorR:
Network$getNodeName
Network$getNodeDescription
Network$getNodePosition
Network$getNodeBgColor
Network$getNodeTextColor
Network$getNodeBorderColorC#:
Network.GetNodeName
Network.GetNodeDescription
Network.GetNodePosition
Network.GetNodeBgColor
Network.GetNodeTextColor
Network.GetNodeBorderColor

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

36

Using SMILE Wrappers
6.4

Multidimensional arrays

Conditional probability table (CPT) describes the interaction between a node and its immediate predecessors.
The number of dimensions and the total size of a conditional probability table are determined by the number ofparents, the number of states of each of these parents, and the number of states of the child node. Essentially,
there is a probability for every state of the child node for every combination of the states of the parents. Nodesthat have no predecessors are specified by a prior probability distribution table, which specifies the priorprobability of every state (outcome) of the node.
The conditional probability tables are stored as vectors of doubles that are a flattened version ofmultidimensional tables with as many dimensions as there are parents plus one for the node itself. The order ofthe coordinates reflects the order in which the arcs to the node were created. The most significant (leftmost)coordinate will represent the state of the first parent. The state of the node itself corresponds to the leastsignificant (rightmost) coordinate.
The image below is an annotated screenshot of GeNIe’s node properties window open for the Forecast node in
the model created in Tutorial 1. Forecast has three outcomes and two parents: Success of the venture and Stateof the economy, with two and three outcomes, respectively. Therefore, the total size of the CPT is 2 x 3 x 3 = 18.
The annotation arrows in the image (not part of the actual GeNIe window) show the ordering of the entries in
the linear buffer used internally. The first (or rather, the zero-th, because all indexes in SMILE are zero-based)
element, the one with the value of 0.7 and yellow background, represents P(Forecast=Good | Success of theventure=Success & State of the economy=Up). It is followed by the probabilties for Moderate and Poor
outcomes given the same parent configuration. The next parent configuration is Success of the venture=Success& State of the economy=Flat, and so on.

The node definition returned by Network.getNodeDefinition is a single-dimensional array. If your program
needs to convert between linear and multidimensional coordinates, see Tutorial 3 99 . The code in this tutorial
includes the conversion function.
Note that multidimensional arrays are not used exclusively for CPTs. Other uses include expected utility tables
and marginal probability distributions.

6.5

Input and Output

SMILE supports two types of network I/O: string-based and file-based.
The native format for SMILE networks is XDSL. The format is XML-based and the definition schema is available
at BayesFusion documentation website (http://support.bayesfusion.com/docs/). When writing network in thisformat to file, the .xdsl extension should be used.
XDSL is the only format supported by string I/O methods. File-based methods can read and write other formats.
Depending on the feature parity between SMILE and the 3rd party software using other file types, some of theinformation may be lost.
Java:
Network.readFile

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

37

Using SMILE Wrappers
Network.writeFile
Network.readString
Network.writeString
Python:Network.read_file
Network.write_file
Network.read_string
Network.write_string
R:
Network$readFile
Network$writeFile
Network$readString
Network$writeString
C#:
Network.ReadFile
Network.WriteFile
Network.ReadString
Network.WriteString

6.6

Inference

SMILE includes functions for several popular Bayesian network inference algorithms, including the clustering
algorithm, and several approximate stochastic sampling algorithms. To run the inference , obtain theprobability of evidence currently set in the network, or switch between various inference algorithm
implementations, use the following methods:
Java:
Network.updateBeliefs
Network.probEvidence
Network.setBayesianAlgorithm
Network.getBayesianAlgorithm
Network.setInfluenceDiagramAlgorithm
Network.getInfluenceDiagramAlgorithm
Python:Network.update_beliefs
Network.prob_evidence
Network.set_bayesian_algorithm
Network.get_bayesian_algorithm
Network.set_influence_diagram_algorithm
Network.get_influence_diagram_algorithm
R:
Network$updateBeliefs
Network$probEvidence
Network$setBayesianAlgorithm
Network$getBayesianAlgorithm
Network$setInfluenceDiagramAlgorithm
Network$getInfluenceDiagramAlgorithm

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

38

Using SMILE Wrappers
C#:
Network.UpdateBeliefs
Network.ProbEvidence
Network.BayesianAlgorithm (read/write property)
Network.InfluenceDiagramAlgorithm (read/write property)
The default algorithm for discrete Bayesian Networks is clustering over network preprocessed with relevance.
The output of this algorithm is exact (as opposed to various sampling algorithms also available in the library).
The sampling inference algorithms can be controlled by setting the number of generated samples with the
Network.setSampleCount method. Obviously, the more samples are generated, the more time it takes tocomplete the inference.
Network.updateBeliefs throws an exception with error code -42 if the temporary data structures required tocomplete the inference take too much memory. In such case, or if the inference takes too long, consider taking
advantage of SMILE's relevance reasoning layer. Relevance reasoning runs as a preprocessing step, which canlessen the complexity of later stages of inference algorithms. Relevance reasoning takes the target node set into
account, therefore, to reduce the workload you should reduce the number of nodes set as targets if possible. Notethat by default all nodes are targets (this is the case when no nodes were marked as such). If your network has
1,000 nodes and you only need the probabilities of 20 nodes, by all means call Network.setTarget on them.
If changing the model to use Noisy-MAX 43 nodes is possible, then it's definitely worth trying. The inference
can be performed very efficiently on the networks with Noisy-MAX nodes when Noisy-MAX decomposition isenabled. To enable it, call Network.setNoisyDecompEnabled. If enabled, the Noisy-MAX decomposition runsin the relevance layer and reduces the complexity of the subsequent phases of the inference algorithm. To
further control the decomposition you can call Network.setNoisyDecompLimit, which controls the maximal
number of parents in the temporary structures managed by SMILE during inference.

6.7

User properties

To integrate data specific to your application with SMILE, you can use SMILE’s user properties. The user
properties are arrays of key/value pairs available at the network and node level. The user properties are stored in
the XDSL files. Property name (key) is unique for the set of properties defined for given node or for a network. It
also follows the convention of SMILE identifiers: is case-sensitive, starts with a letter and contains letters, digits
and underscores. The property is represented by an UserProperty object, which has name and value fields(both are strings).
Java:
Network.getUserProperties
Network.setUserProperties
Network.getNodeUserProperties
Network.setNodeUserProperties
Python:Network.get_user_properties
Network.set_user_properties
Network.get_node_user_properties
Network.set_node_user_properties
R:
Network$getUserProperties
Network$setUserProperties

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

39

Using SMILE Wrappers
Network$getNodeUserProperties
Network$setNodeUserProperties
C#:
Network.GetUserProperties
Network.SetUserProperties
Network.GetNodeUserProperties
Network.SetNodeUserProperties

6.8

Submodels

For user interface purposes, like making complex network structure easier to understand and navigate, nodes
can be placed in a submodel hierarchy. Each network contains at least one main submodel (which can't bedeleted and is default submodel for new models to be placed in). Other submodels can be added and deleted.
Submodels, except main submodel, have exactly one parent submodel, and can have multiple submodelchildren.
The assignment of nodes to submodels has no effect on inference.
Use the following methods to manage submodels:Java:
Network.addSubmodel
Network.deleteSubmodel
Network.getSubmodelCount
Network.getSubmodel
Network.getMainSubmodel
Network.getMainSubmodelId
Network.getFirstSubmodel
Network.getNextSubmodel
Network.getSubmodelId
Network.setSubmodelId
Network.getSubmodelName
Network.setSubmodelName
Network.getSubmodelPosition
Network.setSubmodelPosition
Network.getSubmodelOfNode
Network.setSubmodelOfNode
Network.getSubmodelOfSubmodel
Network.setSubmodelOfSubmodel
Python:Network.add_submodel
Network.delete_submodel
Network.get_submodel_countNetwork.get_submodel
Network.get_main_submodel
Network.get_main_submodel_id
Network.get_first_submodel
Network.get_next_submodel
Network.get_submodel_id
Network.set_submodel_id
Network.get_submodel_name

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

40

Using SMILE Wrappers
Network.set_submodel_nameNetwork.get_submodel_Position
Network.set_submodel_Position
Network.get_submodel_of_node
Network.set_submodel_of_node
Network.get_submodel_of_submodel
Network.set_submodel_of_submodelR:
Network$addSubmodel
Network$deleteSubmodel
Network$getSubmodelCount
Network$getSubmodel
Network$getMainSubmodel
Network$getMainSubmodelId
Network$getFirstSubmodel
Network$getNextSubmodel
Network$getSubmodelId
Network$setSubmodelId
Network$getSubmodelName
Network$setSubmodelName
Network$getSubmodelPosition
Network$setSubmodelPosition
Network$getSubmodelOfNode
Network$setSubmodelOfNode
Network$getSubmodelOfSubmodel
Network$setSubmodelOfSubmodelC#:
Network.AddSubmodel
Network.DeleteSubmodel
Network.GetSubmodelCount
Network.GetSubmodel
Network.GetMainSubmodel
Network.GetMainSubmodelId
Network.GetFirstSubmodel
Network.GetNextSubmodel
Network.GetSubmodelId
Network.SetSubmodelId
Network.GetSubmodelName
Network.SetSubmodelName
Network.GetSubmodelPosition
Network.SetSubmodelPosition
Network.GetSubmodelOfNode
Network.SetSubmodelOfNode
Network.GetSubmodelOfSubmodel
Network.SetSubmodelOfSubmodel

6.9

Discrete nodes and numeric domains

By default, discrete nodes represent discrete random variables that have categorical outcomes described byoutcome identifiers. In SMILE 2, it is possible to model discrete random variables that are numerical in nature.
The outcomes of the node can be associated with adjacent numeric intervals with explicitly specified borders, or

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

41

Using SMILE Wrappers
with a set of discrete numeric values (one for each outcome). There are four possible outcome types for discretenodes:· identifiers (the default)· identifiers and numeric intervals· numeric intervals with no identifiers· identifiers and numeric point values

6.9.1

Outcome intervals

Discrete nodes by default do not have any numeric information associated with their outcomes. To add outcome
intervals, call Network.setIntervals:
Java:
net.setIntervals(nodeHandle, new double[] { 0, 2.78, 3.14, 77, Double.POSITIVE_INFINITY }, true);
Python:net.set_intervals(nodeHandle, [ 0, 2.78, 3.14, 77, math.inf ], True)R:
net$setIntervals(nodeHandle, c({ 0, 2.78, 3.14, 77, Inf }), TRUE)
C#:
net.setIntervals(nodeHandle, new double[] { 0, 2.78, 3.14, 77, Double.PositiveInfinity }, true);
The example above passes five numeric interval boundaries , and defines four intervals: 0 to 2.78, 2.78 to 3.14,
3.14 to 77, and 77 to infinity. Using negative infinity as first element in the intervals definition creates open firstinterval. The list of interval boundaries has to be sorted from lowest to highest. It is possible to specify a pointinterval by means of two identical numbers as its boundaries.
The second parameter in the Network.setIntervals call is set to true to indicate that existing outcome
identifiers should be discarded. The use of outcome identifiers with outcome intervals is optional. If theidentifiers were discarded, the outcome identifier array will have empty string for each outcome id.
It is possible to call the outcome modifying methods when node has intervals. After Network.addOutcome or
Network.insertOutcome new interval will be created by splitting the existing interval at the appropriate index.
Network.deleteOutcome will remove the interval at the specified index. At any time it is possible to call
Network.setIntervals again with a new list of interval boundaries. In such case, the list of new intervalboundaries may have different size than the existing list.
Other methods for working with intervals are Network.hasIntervals, Network.getIntervals, andNetwork.removeIntervals. The last function removes the interval boundaries from the node definition, and incase there were no outcome identifiers, creates the default set of identifiers. The number of outcomes does notchange.
With intervals defined, it is possible to calculate the mean and standard deviation for the node given themarginal probability distribution over its outcomes. In calculating the moments of the distribution, SMILE
treats it as a continuous distribution, assuming that it is uniform within each of the intervals. Open intervals are
an exception; the distribution over these is assumed to be half-normal scaled to ensure the normal PDF height isequal to the neighboring closed interval's uniform distribution. To retrieve the mean or the standard deviation,
call Network.getNodeMean or Network.getNodeStdDev respectively on the node value object.

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

42

Using SMILE Wrappers
Intervals allow for continuous evidence in discrete nodes. To set continuous evidence, call
Network.setContEvidence The evidence will be stored and will be internally converted into an outcome index
during inference in a discrete Bayesian network or an influence diagram. When discrete node with intervals has
continuous evidence, and the child node is continuous, the exact value of continuous evidence in the parent will
be used to evaluate the child during sampling.

6.9.2

Outcome point values

To define a discrete distribution over numeric node outcomes, use Network.setNodePointValues. The
example below assumes that node has three outcomes:
Java:
net.setPointValues(nodeHandle, new double[] { 3.14, -2.78, 77 });Python:net.set_point_values(nodeHandle, [ 3.14, -2.78, 77 ])R:
net$setPointValues(nodeHandle, c({ 3.14, -2.78, 77 }))C#:
net.SetPointValues(nodeHandle, new double[] { 3.14, -2.78, 77 });The three numeric values in will be associated with existing node outcomes. There is also no requirement forordering point values. The identifiers of the outcomes will not be modified, as nodes with point values always
require outcome identifiers.
Outcome modifying methods like Network.addOutcome, Network.insertOutcome or
Network.deleteOutcome work normally when node has point values. New outcomes will have their pointvalues initialized to zero. The point values can only be changed all at once with another
Network.setPointValues call.
With point values defined, it is straightforward to calculate the mean and standard deviation for the marginal
probability distribution over the outcomes of the node. To retrieve the mean or the standard deviation, call
Network.getNodeMean or Network.getNodeStdDev respectively.
Point values do not allow for continuous evidence in the discrete nodes. Calling Network.setContEvidence
will fail, even if the parameter is equal to one of the defined point values.
To remove point values, call Network.removePointValues. Network.hasPointValues returns true if node
has point values defined.

6.10 Canonical nodes
Canonical probabilistic nodes, such as Noisy-MAX/OR, Noisy-MIN/AND, and Noisy-Adder gates, implemented
by SMILE, are convenient knowledge engineering tools widely used in practical applications. In case of a general
CPT binary node with n binary parents, the user has to specify 2^n parameters, a number that is exponential in
the number of parents. This number can quickly become prohibitive: when the number of parents n is equal to10, we need 1,024 parameters, when it is equal to 20, the number of parameters is equal to 1,048,576, with each
additional parent doubling it. A Noisy-OR model allow for specifying this interaction with only n+1 parameters,
one for each parent plus one more number. This comes down to 11 and 21 for n equal to 10 and 20 respectively.

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

43

Using SMILE Wrappers
Canonical models are not only great tools for knowledge engineering - they also lead to significant reduction incomputation through the independences that they model implicitly. Using canonical gates makes thus model
construction easier but also leads to models that are easier to solve.
To create canonical nodes, pass Network.NodeType.NOISY_MAX or Network.NodeType.NOISY_ADDER to
Network.addNode. Each node type has its own specific attributes accessible through Network class methods,
but otherwise works in exactly the same way as a CPT node (has at least two outcomes, can be used as a parent
or a child wherever the CPT node can, etc).

6.10.1 Noisy-MAX
Noisy-MAX is a generalization of the popular canonical gate Noisy-OR and is capable of modeling interactions
among variables with multiple states. If all the nodes in question are binary, a Noisy-MAX node reduces to a
Noisy-OR node. The Noisy-MAX, as implemented in SMILE, includes an equivalent of negation. By DeMorgan'slaws, the OR function (or its generalization, the MAX function) along with a negation, is capable of expressing
any logical relationship, including the AND (and its generalization, MIN). This means that SMILE's Noisy-MAX
can be used to model the Noisy-AND/MIN functions, as well as other logical relationships.
SMILE's inference algorithm contains special code path for networks with Noisy-MAX nodes, which can speed
up computations significantly. See the Inference 37 section of this manual for details.
To create a Noisy-MAX node, use the NOISY_MAX type with Network.addNode:Java:
net.addNode(Network.NodeType.NOISY_MAX, "node1");
Python:net.add_node(pysmile.NodeType.NOISY_MAX, "node1")
R:
net$addNode(net$NodeType$NOISY_MAX, "node1")
C#:
net.AddNode(Network.NodeType.NoisyMax, "node1");
Noisy-MAX node has discrete outcomes (just like CPT nodes). Noisy-MAX specific attributes are:· an array of conditionally independent (CI) probabilities, which can be set and retrieved by

Network.setNodeDefinition and getNodeDefinition.
· for each of node's parents, an array of parent outcome strengths. Parent outcome strengths enable control of

the order of states of the parent nodes, as they enter their relation with the child. A Noisy-MAX CI table
always follows the order of strengths. Use Network.setNoisyParentStrengths andgetNoisyParentStrengths to modify or read the parent strengths.
As an example, consider a binary Noisy-MAX node with two parents, each with three outcomes. The following
snippet modifies the probabilities and outcome strengths for the second parent (with zero-based index 1).
Java:double[] ci = net.getNodeDefiniton(h);int BASE = 2 * 3;ci[BASE]= 0.1;
ci[BASE + 1] = 0.9;
ci[BASE + 2] = 0.3;

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

44

Using SMILE Wrappers
ci[BASE + 3] = 0.7;
net.setNodeDefiniton(h, ci);
net.setNoisyParentStrengths(h, 1, new int[] { 2, 0, 1 });Python:ci = net.get_node_definition(h)BASE = 2 * 3
ci[BASE]= 0.1
ci[BASE + 1] = 0.9
ci[BASE + 2] = 0.3
ci[BASE + 3] = 0.7
net.set_node_definition(h, ci)
net.set_noisy_parent_strengths(h, 1, [2, 0, 1])R:
ci <- net$getNodeDefiniton(h)BASE <- 2 * 3
ci[BASE + 1] <- 0.1
ci[BASE + 2] <- 0.9
ci[BASE + 3] <- 0.3
ci[BASE + 4] <- 0.7
net$setNodeDefiniton(h, ci)
net$setNoisyParentStrengths(h, 1, c(2L, 0L, 1L))C#:
double[] ci = net.GetNodeDefinition(h);int BASE = 2 * 3;ci[BASE]= 0.1;
ci[BASE + 1] = 0.9;
ci[BASE + 2] = 0.3;
ci[BASE + 3] = 0.7;
net.SetNoisyParentStrengths(h, 1, new int[] { 2, 0, 1 });The value of BASE is calculated as a product of node outcome count and preceding parents' outcome counts (inthis case, there is just one preceding parent with three outcomes). The probabilities for the parent are written
into an array returned by Network.getNodeDefiniton and used in a setNodeDefinition call, because we justwant to modify one parents' probabilities and leave other CI probabilities unchanged. The next step changes theorder of parents' outcomes in relationship to the Noisy-MAX node with
Network.setParentOutcomeStrengths call, the first column of probabilities for parent with index 1 (the one
with 0.1 and 0.9) represents the probabilities for the parent outcome with index 2 (because 2 is the first elementin the strengths array). Note that this does not modify the parent node in any way and the ordering is valid
only in the context of this particular parent-child relationship. Assuming that we started with the default
uniform probabilities in the table, our modifications yields the following Noisy-Max definition, as viewed in
GeNIe:The outcomes of both parent are {State0, State1, State2}. However, by using setParentOutcomeStrenghts forparent p2, its outcomes are seen by the Noisy-MAX child node as {State2, State0, State1}. Other Noisy-MAX
nodes in the same network can set up their own parent outcome ordering if p2 becomes their parent.

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

45

Using SMILE Wrappers
6.10.2 Noisy-Adder
Noisy-Adder nodes are not fully supported yet in the wrappers. Contact us if you need to use them; we canprovide pre-release version of an SMILE wrapper compatible with your programming language and operating
system.

6.11 Influence diagrams
Influence diagrams use two additional node types next to chance (CPT and canonical) and deterministic nodes:· Decision nodes represent variables that are under control of the decision maker and model available decision

alternatives, modeled explicitly as possible states of the decision node. They have no numerical parameters,
only a discrete set of outcomes. Decision nodes can be children of decision and chance nodes. The node typeidentifier passed to Network.addNode is Network.NodeType.DECISION.
· Value nodes, i.e., a measure of desirability of the outcomes of the decision process. They are quantified by theutility of each of the possible combinations of outcomes of the parent nodes. Value nodes can be children ofdecision and chance nodes. Pass Network.NodeType.UTILITY to Network.addNode to create a value node.
· Multi-attribute utility (MAU) nodes, which combine value nodes to form a multi-attribute utility function. The

function can be specified as a set of weights of a linear function (in such case, the node becomes an additive
linear utility, ALU) or any expression that refers to identifiers of the value node parents. See the Equations
reference 52 chapter for a list of available functions. MAU nodes can be children of decision, value, and other
MAU nodes. If decision parents exist, the definition of the MAU node contains a separate set of weights or
expressions for each combination of decision parents. Use Network.NodeType.MAU with Network.addNodeto create a MAU node. By default, the MAU node is defined by weights. To use expressions, call
Network.setMauExpressions.
As is the case with Bayesian networks, the values calculated by influence diagram inference algorithms are
stored in node values and can be accessed through Network.getNodeValue. However, the interpretation of thenumbers returned by getNodeValue is extended. The matrices are indexed by the set of nodes called indexing
parents. These are are unobserved decision nodes that precede the current node or unobserved chance nodes
that are predecessors of decision nodes and should have been observed before the decisions can be made. Call
Network.getIndexingParents to retrieve indexing parent handles, or Network.getIndexingParentIds to
get indexing parent identifiers. The set of outcomes of indexing parents is called a policy. After a successful
inference in an influence diagram, node values are:· for chance and deterministic nodes: posterior probabilities for each policy· for decision nodes: expected utilities for all outcomes and for each policy· for value and MAU nodes: expected utility for each policy

See Tutorial 4 108 for a simple influence diagram demo program.

6.12 Dynamic Bayesian networks
A Bayesian network is a snapshot of the system at a given time and is used to model systems that are in somekind of equilibrium state. Unfortunately, most systems in the world change over time and sometimes we areinterested in how these systems evolve over time more than we are interested in their equilibrium states.

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

46

Using SMILE Wrappers
Whenever the focus of our reasoning is change of a system over time, we need a tool that is capable of modelingdynamic systems.
A dynamic Bayesian network (DBN) is a Bayesian network extended with additional mechanisms that arecapable of modeling influences over time (Murphy, 2002). The temporal extension of BNs does not mean thatthe network structure or parameters changes dynamically, but that a dynamic system is modeled. In otherwords, the underlying process, modeled by a DBN, is stationary. A DBN is a model of a stochastic process.
The implementation of DBNs in SMILE allows for use both chance (CPT and canonical) and deterministic nodetypes in dynamic models.
Tutorial 6 122 contains a complete program demonstrating the use of DBNs.

6.12.1 Unrolling
SMILE's inference algorithm for dynamic Bayesian networks converts DBNs (unrolls them over time) totemporary static networks and then solves these networks. Results of this inference are copied back into thenode values in the original DBNs. The structure of the following DBN (in GeNIe format) does not represent any
real system, it is created just for demonstration purposes.

Different colors of the nodes represent their location relative to the abstract "temporal plate." By default, thenodes in the network are set to be Network.NodeTemporalType.CONTEMPORAL and is located outside of theplate. By using Network.setNodeTemporalType you can change their temporal type assignment. In theexample model above, the plate nodes (Network.NodeTemporalType.PLATE) are white, the anchor node
(Network.NodeTemporalType.ANCHOR) is green, the terminal node (Network.NodeTemporalType.TERMINAL)
is orange and the blue node is contemporal. For simplicity, we use only single anchor, terminal, andcontemporal node. Note the presence of multiple arcs between two plate nodes and the arc linking P1 with itself.

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

47

Using SMILE Wrappers
Some of the arcs between plate nodes have a tag with a number; these are temporal arcs, which are created by
Network.addTemporalArc. The number on the tag is a temporal order of an arc. The arcs without the tag wereadded by Network.addArc. The result of unrolling the above DBN (this is performed automatically by the DBN
inference algorithm; it is possible to create such network with Network.unroll) is the following:

To reduce the size of the unrolled network, we changed the number of time steps (slices) from the default valueof 10 to 4 with a call to Network.setSliceCount. The colors of the original nodes were carried over to theunrolled model, which is extended by creating new copies of the plate nodes. The structure of this networkshows how the temporal arcs are used to express the conditional dependency of plate nodes in time step i on theplate nodes in time step j, where i > j.
Consider the (temporal) arc between P1 and P2 with temporal order 2. It was copied twice to link P1 with P2(t=2), and P1 (t=1) with P2 (t=3). Note that there is no copy of this arc starting from P1 (t=2), because its childwould be in t=2+2=4, and (zero-based) time stops at 3 in our example. On the other hand, all three temporalarcs with temporal order 1 are copied three times. The relationship represented by an arc linking P1 with itself in
the DBN is clearly visible between P1 and P1 (t=1), P1 (t=1) and P1 (t=2) and P1 (t=2) and P1 (t=3).
Note the difference between the arcs originating in Contemporal and Anchor nodes. The Anchor node has
children only in the initial slice, while the Contemporal node is linked to all time slices. The Terminal node has
parents only in the last slice.
To ensure that unrolled network remains a directed acyclic graph, the following arcs are forbidden in DBNs:· from plate nodes to normal and anchor nodes· from terminal nodes to non-terminal nodes

Unrolling is performed automatically during inference. For debug/explanatory purposes it is also possible toobtain an unrolled network by Network.unroll. The unrolled network created this way is an independent
model, which means that changes made to the DBN after unroll call are not propagated into the unrolled
network.

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

48

Using SMILE Wrappers
6.12.2 Temporal definitions
Consider node P2 from the example in the previous section. It has four incoming arcs:· normal arc from Contemporal· normal arc from P1· two temporal arcs from P1 with temporal orders 1 and 2

However, in the unrolled network's slice for t=0, P2 has only two incoming arcs. This is because there are noslices representing timepoints before t=0. P2 in the slice for t=1 has three incoming arcs, because it's now
possible to link P1 at t=0 with P2 at t=1. Finally, starting with slice for t=2, P2 has four incoming arcs. The
structure of the unrolled network requires P2 to have separate CPTs for t=0, t=1 and t>=2. Generally speaking, if
a plate node has an incoming arc of order x, it will require x+1 separate definitions. To get and set the temporaldefinitions, use Network.getNodeTemporalDefinition and setNodeTemporalDefinition.
All parents for the temporal definition with a specified temporal order can be retrieved by calling
Network.getUnrolledParents. Note that Network.getTemporalParents called for the same temporal orderreturns only a subset of parents indexing this temporal definition. This is caused by unrolling: in the unrolled
network node P2 has four incoming arcs in slices for t>=2, but only two of these are actually arcs with temporalorder 2.

6.12.3 Temporal evidence
To specify evidence for the plate nodes in the DBN, use Network.setTemporalEvidence and
setTemporalVirtualEvidence. The code snippet below sets the temporal evidence in slices 5 and 7. Noderepresented by evidenceNodeHandle is assumed to have two outcomes.
Java:
net.setTemporalEvidence(evidenceNodeHandle, 5, 1);
net.setTemporalVirtualEvidence(evidenceNodeHandle, 7,
new double[] { 0.4, 0.6 });Python:net.set_temporal_evidence(evidenceNodeHandle, 5, 1)
net.set_temporal_virtual_evidence(evidenceNodeHandle, 7, [0.4,0.6])
R:
net$setTemporalEvidence(evidenceNodeHandle, 5, 1)
net$setTemporalVirtualEvidence(evidenceNodeHandle, 7,c(0.4, 0.6))
C#:
net.SetTemporalEvidence(evidenceNodeHandle, 5, 1);
net.SetTemporalVirtualEvidence(evidenceNodeHandle, 7,
new double[] { 0.4, 0.6 });To retrieve the temporal evidence, use Network.getTemporalEvidence andNetwork.getTemporalVirtualEvidence.

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

49

Using SMILE Wrappers
Other useful methods are Network.hasTemporalEvidence and isTemporalEvidence, which check whether anode has any temporal evidence or temporal evidence in specified temporal order, respectively.

6.12.4 Temporal beliefs
For plate nodes in the DBN, the inference algorithm calculates the temporal beliefs, which are marginal
posterior probability distributions as a function of time. Temporal beliefs are returned by
Network.getNodeValue.
The dependency of the beliefs on time makes the temporal beliefs array larger. If a node has X outcomes and theslice count was set to Y, the matrix will have X * Y elements. The elements representing a single time slice are
adjacent. Therefore, elements with indices [0..X-1] in the matrix are the beliefs for t=0, elements [X..2*X-1] are
the beliefs for t=1 and so on.
Only the plate nodes have the temporal beliefs. Other temporal node types have normal beliefs (the number of
elements in the belief array is equal to their outcome count).

6.13 Continuous models
Graphical models, such as Bayesian networks, are not necessarily consisting of only discrete variables. They are,in fact, close relatives of systems of simultaneous structural equations. SMILE allows for constructing models
consisting of equation nodes that are alternative, graphical representations of systems of simultaneous
structural equations.
Tutorial 7 133 contains a complete program demonstrating the use of continuous models.

6.13.1 Equation-based nodes
To create an equation node, use Network.NodeType.EQUATION node type when creating a node with
Network.addNode. The node equation will be initialized to id=0, where id is the node identifier passed to
addNode. The code snippet below changes the default equation of the freshly created node (x=0) to an equation
representing the standard Gaussian distribution: x=Normal(0, 1).
Java:
int hx = net.addNode(Network.NodeType.EQUATION, "x");net.setNodeEquation(hx, "x=Normal(0,1)");
Python:hx = net.add_node(pysmile.NodeType.EQUATION, "x")net.set_node_equation(hx, "x=Normal(0,1)")R:hx <- net$addNode(net$NodeType$EQUATION, "x")net$setNodeEquation(hx, "x=Normal(0,1)")

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

50

Using SMILE WrappersC#:
int hx = net.AddNode(Network.NodeType.Equation, "x");net.SetNodeEquation(hx, "x=Normal(0,1)");
SMILE defines many functions for use in node equations. The complete list of functions is available in the
Equations reference 52 section. Among these functions, there are random generators, which generate a single
sample based on the passed parameters. In the example above, the Normal is the name of SMILE's random
generator function.
Node equations can reference other nodes by using their identifiers. Continuing with our code snippet:Java:
int hy = net.addNode(Network.NodeType.EQUATION, "y");net.setNodeEquation(hx, "y=2*x");
Python:hy = net.add_node(pysmile.NodeType.EQUATION, "y")net.set_node_equation(hy, "y=2*x")R:hy <- net$addNode(net$NodeType$EQUATION, "y");net$setNodeEquation(hx, "y=2*x");C#:
int hy = net.AddNode(Network.NodeType.Equation, "y");net.SetNodeEquation(hx, "y=2*x");
Second node is added and its equation is set to y=2*x, where x is a reference to previously defined node. SMILE
adds an arc between x and y automatically and it is not necessary to call Network.addArc before setting theequation referencing other nodes. Calling addArc will change the child node equation by adding a term
representing the parent node as a last term on the right hand side of the child equation. Assuming network withequation nodes a, b, c and d and d's equation set to d=Normal(a, 1)+Normal(b, 2), calling AddArc to with node
handles of c and d would rewrite d's equation to d=Normal(a, 1)+Normal(b, 2)+c.
If an arc is removed, either by calling Network.deleteArc or Network.deleteNode on one of the parents, thenode equation will be rewritten as sum of the remaining parents. This ensures that equations and arcs are always
in sync. If node b would be removed with deleteNode, or an arc from b to d would be removed by deleteArc,
d's equation would become d=a+c.
Node identifier changes are propagated into the equations. If the identifier of the first node from the codesnippet above was changed from x to x_prime, the equation of node y would change to y=2*x_prime.

6.13.2 Continuous inference
To run the inference in continuous model, use Network.updateBeliefs; the same method which is used indiscrete networks.
To set evidence in an equation node, use Network.setContEvidence. In the snippet below we are assumingthat evidenceNodeHandle is the handle of the equation node.
Java:
net.setContEvidence(evidenceNodeHandle, 1.5);
Python:SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

51

Using SMILE Wrappers
net.set_cont_evidence(evidenceNodeHandle, 1.5)
R:
net$setContEvidence(evidenceNodeHandle, 1.5)
C#:
net.SetContEvidence(evidenceNodeHandle, 1.5);
To retrieve evidence from continuous node, use Network.getContEvidence.
The inference in continuous networks is based on stochastic sampling when there is no evidence in the network,or the evidence is specified only for nodes without parents. Otherwise, the inference is performed on atemporary discrete network, derived from the original continuous model. The definitions for the temporary
discrete nodes are derived from the discretization intervals defined in each continuous node. In addition to
Network.setSampleCount, stochastic sampling can be controlled at the network level by setting thediscretization sample count with Network.setDiscretizationSampleCount. Large number of samples
provides better approximation of the solution to the set of equations, which continuous network represents, but
requires more time to complete.
Both types of inference algorithm use the lower and upper bounds defined for each equation node. To set thebounds, use Network.setNodeEquationBounds method. Stochastic sampling can reject a sample when its
value falls outside of the bounds defined for the node. You can control this behavior by
Network.setOutlierRejectionEnabled. By default, outlier rejection is disabled.
To set node's discretization intervals, use Network.setNodeEquationDiscretization. The method accepts anarray of DiscretizationInterval objects intervals as its argument. Each interval is defined by its optional
identifier (not used for inference) and an upper bound. The lower bound of the interval with index j is defined byupper interval of the interval with index j-1. The lower bound of the first interval is defined by the lower bounddefined for the node with a Network.setNodeEquationBounds call.
Discretization intervals are used to obtain CPTs for the temporary discrete network. The CPTs are calculated bydrawing a number of samples specified at the network level for each CPT column. The size of the discretized CPT
is a product of the number of node intervals and parent node intervals. Note that this may lead to excessive
memory use when the node has many parents.
The results of the inference in a continuous model can be either samples or discretized beliefs, depending on theinference algorithm:· sampling algorithm outputs samples for each node. Network.getNodeValue will return an array of samples

and Network.getNodeSampleStats returns an array with simple statistics for the sample set (mean,
standard deviation, minimum and maximum. Network.isValueDiscretized returns false.
· discretizing algorithm outputs probability distribution over discretization intervals. Network.getNodeValue

returns this distribution. Network.isValueDiscretized returns true.

6.14 Hybrid models
Hybrid models are networks with both discrete and continuous nodes. The arcs in a hybrid network can link all
combinations of node types, so it is possible to add an arc from a continuous to a discrete node, and from a
discrete to a continuous node. Iinference in hybrid models follows the rules defined for continuous nodes(sampling when there is no evidence in nodes with parents, discretization otherwise). Basically, hybrid models
can be treated as continuous models with discrete nodes representing the specialized function, namely
conditional probability table specified by an array of numbers.

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

52

Using SMILE Wrappers
Adding arcs from discrete to continuous nodes is performed by including discrete node identifier in thecontinuous node equation (as is the case for the continuous to continuous arcs). The following discussion
assumes that reader is familiar with the functions from which the node equations are built, described in detail in
the Equations reference 52 section of the reference part of this manual.
For example, assuming that a node c is continuous and a node d is discrete, the equation for c may look like this:c=Normal(If(d=1, 1, -1), 5). When a sample for node c is evaluated, one of its inputs will be the value of its parent
node d. Discrete node values are numbers drawn from the interval [0 .. N-1], where N is the number of discretenode outcomes. Therefore, the example equation for c above says that c should be drawn from a normal
distribution with standard deviation equal to 5, but with mean depending on the parent node d. If d is in its(zero-based) state with index 1, the mean will be 1 and -1 otherwise.
To improve the readability of the equation, the outcomes of the parent discrete nodes can also be represented as
text literals. If d has three outcomes High, Medium and Low, then the equation from the preceding example
could be rewritten as c=Normal(If(d="Medium"), 1, -1), 5).
In addition to the function If or its counterpart, the ternary operator ?:, the common functions to use withdiscrete nodes are Switch and Choose. For example, the equation for node c with three possible means of its
normal distributions can look like this: c=Normal(Switch(d, "High", 3.2, "Medium", 2.5, "Low", 1.4), 5). An
alternative notation would be c=Normal(Choose(d, 3.2, 2.5, 1.4), 5).
Important: SMILE will not modify the text literals representing the outcomes of discrete parent nodes if theoutcome identifiers change. If the text literal cannot be associated with any parent node outcome, it's value isevaluated as -1 (minus one).
Generally speaking, discrete nodes can appear anywhere in the equation where numbers can be used: c=log(1+d)
or c=2^d. This kind of equation does not use the text literals representing the discrete node outcomes (because
there is no comparison involved in evaluation the equation).
To add an arc from a continuous to a discrete node, use Network.addArc (the method used in discrete models).
In such case, the discretization intervals of the parent node are considered to be the equivalent of the outcomesof discrete parent.
Tutorial 8 143 contains a complete program demonstrating the use of hybrid models.

6.15 Equations reference
The subsections of this chapter describe the notation used when specifying the definition for equation nodes andexpression-based MAU nodes.

6.15.1 Operators
Arithmetic operators+-

addition, e.g., if x=3 and y=2, x+y produces 5^

exponentiation (a^b means ab), e.g., if x=3 and y=2, x^y produces 9

subtraction and unary minus, e.g., if x=3 and y=2, x-y produces 1 and -x produces -3

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

53

Using SMILE Wrappers*

multiplication, e.g., if x=3 and y=2, x*y produces 6/

division, e.g., if x=3 and y=2, x/y produces 1.5

Comparison operators>

greater than, e.g., if x=3 and y=2, x>y produces 1<

smaller than, e.g., if x=3 and y=2, x<y produces 0>=

greater or equal than, e.g., if x=3 and y=2, x>=y produces 1<=

smaller or equal than, e.g., if x=3 and y=2, x<=y produces 0<>

not equal, e.g., if x=3 and y=2, x<>y produces 1=

equal, e.g., if x=3 and y=2, x=y produces 0

Conditional selection operator?:

ternary conditional operator like in C, C++ or Java programming languages.

This operator is essentially a shortcut to the If function. For example, a=b?5:3 is equivalent to If(a=b, 5, 3).

Order of calculation, operator precedence, and parentheses
Expressions are evaluated from left to right, according to the precedence order specified below (1 denotes thehighest precedence).
Precedence
order

Operator1- (unary minus)

2^ (exponentiation)3* and / (multiplicative operators)

4+ and - (additive operators)

5>, <, >=, <=, = (comparison
operators)

6

SMILE Wrappers?: (conditional selection)

Version 2.2.4.R1, Built on 4/27/2024

54

Using SMILE Wrappers
To change the order of calculation, enclose in parentheses those parts of the formula that should be calculated
first. This can be done recursively, i.e., parentheses can be nested indefinitely. For example, if x=3 and y=2,
2*y+x/3-y+1 produces 4, 2*(y+x)/(3-y)+1 produces 11, and 2*((y+x)/(3-y)+1) produces 12.

6.15.2 Random Number Generators
Random number generators each generate a single sample from the distributions defined below. In mostequations, they can be imagined as random noise that distorts the equation. Because the fundamental algorithm
for inference in continuous and hybrid models is stochastic simulation, it is possible to visualize what probability
distributions these single samples result in for each of the variables in the model.
Caution: Probability distributions are not allowed in expression-based MAU nodes, as these aredeterministic functions by definition.
Choosing the right probability distribution over continuous data is a skill that requires some statistical insight.
When the distribution is transformed by an equation expression, the task is daunting even for an experienced
decision analyst. GeNIe offers an interactive tool for visualizing expressions, the Distribution Visualizer, withprobability distributions through Monte Carlo simulation. The same functionality is available online athttps://prob.bayesfusion.com. The screenshot below shows the online visualizer with the probability
distribution sampled from the expression Log10(Sqrt(If(Uniform(0,1)<1/4,Lognormal(1,0.3),Lognormal(0, 0.2)))).

Bernoulli(p)SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

55

Using SMILE Wrappers
Bernoulli is a discrete distribution that generates 0 with probability 1-p and 1 with probability p.
Bernoulli(0.2) will generate a single sample (0 or 1) from the following distribution, i.e., 1 with probability 0.2
and 0 with probability 0.8:

Beta(a,b)
The Beta distribution is a family of continuous probability distributions defined on the interval [0, 1] andparametrized by two positive shape parameters, a and b (typically denoted by α and β), that control the shape ofthe distribution. Beta(2,5) will generate a single sample from the following distribution:

Binomial(n,p)
Binomial is a discrete probability distribution over the number of successes in a sequence of n independent
trials, each of which yields a success with probability p. It will generate a single sample, which will be an integer
number between 0 and n. A success/failure experiment is also called a Bernoulli trial. Hence, Binomial(1,p) is
equivalent to Bernoulli(p). Binomial(100,0.3) will generate a single sample from the following distribution:

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

56

Using SMILE Wrappers

CustomPDF(x1,x2,...y1,y2,...)The CustomPDF distribution allows for specifying a non-parametric continuous probability distribution bymeans of a series of points on its probability density (PDF) function. Pairs (xi,yi) are coordinates of such points.
The total number of parameters of CustomPDF function should thus to be even. Please note that x coordinates
should be listed in increasing order. The PDF function specified does not need to be normalized, i.e., the area
under the curve does not need to add up to 1.0. For example, CustomPDF(0,1.02,1.9,3.2,10,0,4,5,1,0)
generates a single sample from the following distribution:

Stars on the plot mark the points defined by the CustomPDF arguments, i.e., (0, 0), (1.02, 4), (1.9, 5), (3.2, 1), and(10, 0).

Discrete(x1,x2,...,xn, p1,p2,...,pn)
The Discrete distribution allows for specifying a discrete probability distribution over a collection of numericalvalues. It is one of the simplest random number generators, essentially replicating a discrete distribution andproducing values x1, x2, ..., xn with probabilities p1, p2, ..., xn. This distribution is useful in simulating a discretenode using an equation node. The total number of parameters of Discrete() function should be even. Please
note that x values should be listed in increasing order. Even though the p values should in theory add up to 1.0
and we advise that they do, GeNIe perform normalization, i.e., modifies them proportionally to add up to 1.0.
For example, Discrete(15,40,57.5,72.5,0.137339,0.397711,0.387697,0.0772532) replicates the

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

57

Using SMILE Wrappers
definition of the variable Age in the HeparII model and generates a single sample from the following
distribution:

Exponential(lambda)
The exponential distribution is a continuous probability distribution that describes the time between events in a
Poisson process, i.e., a process in which events occur continuously and independently at a constant average rate.
Its only real-valued, positive parameter lambda (typically denoted by λ) determines the shape of the
distribution. It is a special case of the Gamma distribution. Exponential(lambda) generates a single sample
Exponential(1) will generate a single sample from the following distribution:

Gamma(shape,scale)
The Gamma distribution is a two-parameter family of continuous probability distributions. There are different
parametrizations of the Gamma distribution in common use. SMILE parametrization follows one of the mostpopular parametrizations, with shape (often denoted by k) and scale (often denoted by θ) parameters, both
positive real numbers. Gamma(2.0,2.0) will generate a single sample from the following distribution:

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

58

Using SMILE Wrappers

Lognormal(mu,sigma)
The lognormal distribution is a continuous probability distribution of a random variable, whose logarithm isnormally distributed. Thus, if a random variable X is lognormally distributed, then a variable Y=Ln(X) has anormal distribution. Conversely, if Y has a normal distribution, then X=eY has a lognormal distribution. A
random variable which is lognormally distributed takes only positive values. Lognormal(0,0.5) will generate asingle sample from the following distribution:

Metalog(lower,upper,k,x1,x2,...,y1,y2,...)
Metalog (also known as the Keelin) distribution is a very flexible distribution, capable of fitting many naturally
occurring distributions. It can be specified by probability quantiles, which are values of the variable xi and theircorresponding cumulative probabilities yi. Metalogs are able to represent distributions that are unbounded,semi-bounded, and bounded. lower and upper are the bounds of the distribution (-Inf() and Inf() denotelower and upper infinite bound respectively). k is a parameter of the metalog distribution, running from 2 to n,
where n is the number of probability quantiles specified. Generally the higher the value of k, the more flexible
the distribution but it is worth looking at the distributions generated for different values of k to find acompromise between complexity and goodness of fit. The choice of k is best performed interactively, looking at
the family of metalog distributions generated from the probability quantiles. GeNIe contains a built-in tool, theMetalog Builder, which displays metalog PDF and CDF charts. This functionality is also available online athttps://metalog.bayesfusion.com.

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

59

Using SMILE Wrappers
The examples below use identical bounds and probability quantiles with k equal 4, 6 and 8:
Metalog(0,Inf(),4,3,4,5,5,7,10,12,15,18,32,0.001,0.01,0.05,0.1,0.25,0.5,0.75,0.9,0.95,0.999) will generate a single sample from the following distribution:

Metalog(0,Inf(),6,3,4,5,5,7,10,12,15,18,32,0.001,0.01,0.05,0.1,0.25,0.5,0.75,0.9,0.95,0.999) will generate a single sample from the following distribution:

Metalog(0,Inf(),8,3,4,5,5,7,10,12,15,18,32,0.001,0.01,0.05,0.1,0.25,0.5,0.75,0.9,0.95,0.999) will generate a single sample from the following distribution:

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

60

Using SMILE Wrappers

For more information about metalog distributions, please look at the comprehensive article on the topic on
Wikipedia (https://en.wikipedia.org/wiki/Metalog_distribution), the Metalog Distribution web site created by
Tom Keelin (http://metalogdistributions.com/) or the Metalog Distributions YouTube channel(https://www.youtube.com/channel/UCyHZ5neKhV1mSsedzDBoqyA).

MetalogA(lower,upper,a1,a2,...)
MetalogA function uses what one could call internal metalog coefficients (ai and, additionally, the lower andupper bound of the distribution) that, contrary to percentiles of the distribution used as parameters of Metalog,
do not have easily interpretable meaning. One might expect that MetalogA is more efficient in sample
generation, as it skips the whole process of deriving the distribution from which it subsequently generates asample. However, SMILE has an efficient caching scheme that makes Metalog equally efficient in practice.
MetalogA(0,Inf(),2.30769,0.164148,-0.731388,0.343231,0.883249,-0.170727,1.40341,2.64853),
which is equivalent toMetalog(0,Inf(),8,3,4,5,5,7,10,12,15,18,32,0.001,0.01,0.05,0.1,0.25,0.5,0.75,0.9,0.95,0.999), will generate a single sample from the following distribution:

Please note that the number of ai parameters of MetalogA is the same as the k parameter in Metalog. Obtaining
the parameters ai outside of tools like Metalog Builder is rather challenging.

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

61

Using SMILE Wrappers
Normal(mu,sigma)
Normal (also known as Gaussian) distribution is the most commonly occurring continuous probability
distribution. It is symmetric and defined over the real domain. Its two parameters, mu (mean, µ ) and sigma(standard deviation, σ), control the position of its mode and its spread respectively. Normal(0,1) will generate asingle sample from the following distribution:

Poisson(lambda)
Poisson distribution is a discrete probability distribution typically used to express the probability of a given
number of events occurring in a fixed interval of time or space if these events occur with a known constant rateand independently of the time since the last event. Its only parameter, lambda, is the expected number ofoccurrences (which does not need to be integer). Poisson(4) will generate a single sample from the following
distribution:

Steps(x1,x2,...y1,y2,...)The Steps distribution allows for specifying a non-parametric continuous probability distribution by means of aseries of steps on its probability density (PDF) function. It is similar to the CustomPDF function, although it
does not specify the inflection points but rather intervals and the height of a step-wise probability distribution in

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

62

Using SMILE Wrappers
each of the intervals. Because the number of interval borders is always one more than the number of intervals
between them, the total number of parameters of Steps function should be odd. Please note that x coordinates
should be listed in increasing order. The PDF function specified does not need to be normalized, i.e., the area
under the curve does not need to add up to 1.0.
Example: Steps(0,1,2,3,4,1,2,3,4) generates a single sample from the following distribution::

Triangular(min,mod,max)
Triangular distribution is a continuous probability distribution with lower limit min, upper limit max and mode
mod, where min mod max. Triangular(0,1,3) will generate a single sample from the following
distribution:

TruncNormal(mu,sigma,lower[,upper])Truncated Normal distribution is essentially a Normal distribution that is truncated at the values lower andupper. This distribution is especially useful in situation when we want to limit physically impossible values in themodel.
TruncNormal(3,2,1) will generate a single sample from the following distribution:

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

63

Using SMILE Wrappers

TruncNormal(0,1,5,10) will generate a single sample from the following distribution:

Uniform(a,b)
The continuous uniform distribution, also known as the rectangular distribution, is a family of probability
distributions under which any two intervals of the same length are equally probable. It is defined two
parameters, a and b, which are the minimum and the maximum values of the random variable. Uniform(0,2)
will generate a single sample from the following distribution:

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

64

Using SMILE Wrappers

Weibull(lambda,k)Weibull distribution is a continuous probability distribution named after a Swedish mathematician Waloddi
Weibull, used in modeling such phenomena as particle size. It is characterized by two positive real parameters:
the scale parameter lambda (λ) and the shape parameter k. Weibull(1,1.5) will generate a single sample fromthe following distribution:

6.15.3 Statistical Functions
Erf(x)
Returns error function ((also called the Gauss error function, inverse of the Normal PDF), e.g., Erf(1.0)=0.842701.

NormalPDF(x, mu, sigma)
Returns the value of PDF for the normal distribution given by mu and sigma at x.

NormalCDF(x, mu, sigma)

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

65

Using SMILE Wrappers
Returns the value of CDF for the normal distribution given by mu and sigma at x.

6.15.4 Arithmetic Functions
Abs(x)
Returns the absolute value of a number, e.g., Abs(5.3)=Abs(-5.3)=5.3.

Exp(x)
Returns e (Euler's number) raised to the power of x, e.g., Exp(2.2)=e2.2=9.02501.

Gammaln(x)Returns the natural logarithm of the Gamma function (Γ(x)), e.g., Gammaln(2.2)=0.0969475.

GCD(n,k)Returns the greatest common integer divisor of its two integer arguments n and k, e.g., GCD(15,25)=5. Whenthe arguments are not integers, their fractional part is ignored.

Inf()
Returns positive infinity. For negative infinity, please use -Inf().

LCM(n,k)Returns the least common integer multiple of its two integer arguments n and k, e.g., LCM(15,25)=75.When thearguments are not integers, their fractional part is ignored.

Ln(x)Returns the natural logarithm of x, which has to be non-negative, e.g., Ln(10)=2.30259.

Log(x,b)Returns the base b logarithm of x, which has to be non-negative, e.g., Log(10,2)=3.32193.

Log10(x)Returns decimal logarithm of x, which has to be non-negative, e.g., Log10(100)=2.

Mod(x,y)Returns the remainder of dividing x by y. This works for both integer and real arguments, e.g., Mod(5,2)=1,
Mod(9.2,3.7)=1.8.

Pow10(x)SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

66

Using SMILE Wrappers
Returns 10 raised to the power of x, e.g., Pow10(2)=102=100.

Round(x)Returns the integer that is nearest to x, e.g., Round(2.2)=2, Round(3.5)=4.

Sign(x)Returns 1 if x>0, 0 when x=0, and -1 if x<0, e.g., Sign(2.2)=1, Sign(0)=0, Sign(-3.5)=-1.

Sqrt(x)Returns the square root of x, which has to be non-negative, e.g., Sqrt(2)=1.41421.

SqrtPi(x)Returns square root of π multiplied by x, which has to be non-negative, e.g. SqrtPi(2)=Sqrt(Pi()*2)=2.50663. This function is provided for the sake of compatibility with Microsoft Excel.

Sum(x1,x2,...)
Returns the sum of its arguments, e.g., Sum(2.2, 3.5, 1.3)=7.0. Sum() requires at least two arguments.

SumSq(x1,x2,...)
Returns the sum of squares of its arguments, e.g., SumSq(2.2, 3.5, 1.3)=18.78. SumSq() requires at least
two arguments.

Trim(x,lo,hi)
Trims the value of the argument x to a value in the interval <lo, hi>. If x lo, the function returns lo, if x hi, thefunction returns hi, if lo<x<hi, the function returns x. The function is a shortcut to two nested conditional
functions If() and is equivalent to If(x<lo,lo,If(x>hi,hi,x)). For example, Trim(-0.5,0,1)=0,
Trim(0.5,0,1)=0.5, Trim(1.5,0,1)=1.

Truncate(x)Returns the integer part of x, e.g., Truncate(2.2)=2.

6.15.5 Combinatoric Functions
Combin(n,k)
Returns the number of combinations of distinct k elements from among n elements, e.g., Combin(10,2)=45.

Fact(n)Returns the factorial of n, e.g., Fact(5)=5!=120. Fact of a negative number returns 0.

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

67

Using SMILE Wrappers
FactDouble(n)Returns the product of all even (when n is even) or all odd (when n is odd) numbers between 1 and n, e.g.,
FactDouble(5)=15, FactDouble(6)=48. FactDouble of a negative number returns 0.

Multinomial(n1,n2,...)
Factorial of sum of arguments, divided by the factorials of all arguments, e.g., Multinomial(2,5,3)=Fact(2+5+3)/(Fact(2)*Fact(5)*Fact(3)=10!/(2!*5!*3!)=2520. All arguments of Multinomial haveto be positive.

6.15.6 Trigonometric Functions
Acos(x)Returns arccosine (arcus cosinus) of x, e.g., Acos(-1)=3.14159.

Asin(x)Returns arcsine (arcus sinus) of x, e.g., Asin(1)=1.5708.

Atan(x)Returns arctangent (arcus tangens) of x, e.g., Atan(1)=0.785398.

Atan2(y,x)
Returns arctangent (arcus tangens) from x and y coordinates, e.g., Atan2(1,1)=0.785398.

Cos(x)
Returns cosine (cosinus) of x, e.g., Cos(1)=0.540302.

Pi()
Returns constant π, e.g., Pi()=3.14159.

Sin(x)
Returns sine (sinus) of x, e.g., Sin(1)=0.841471.

Tan(x)
Returns tangent (tangens) of x, e.g., Tan(1)=1.55741.

6.15.7 Hyperbolic Functions
Cosh(x)SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

68

Using SMILE Wrappers
Returns the hyperbolic cosine of x, e.g., Cosh(1)=1.54308.

Sinh(x)Returns the hyperbolic sine of x, e.g., Sinh(1)=1.1752.

Tanh(x)Returns the hyperbolic tangent of x, e.g., Tanh(1)=0.761594.

6.15.8 Logical/Conditional functions
And(b1,b2,...)
Returns the logical conjunction of the arguments, which are all interpreted as Boolean expressions. If any of theexpressions evaluates to a zero, And returns 0. For example, And(1=1,2,3)=1, And(1=2,2,3)=0.

Choose(index,v0,v1,...,vn)
Returns vi if index is equal to i. When index evaluates to a value smaller than 0 or larger than n-1, the function
returns 0. Examples:
Choose(0,1,2,3,4,5)=1
Choose(4,1,2,3,4,5)=5
Choose(7,1,2,3,4,5)=0

If(cond,tval,fval)
If cond evaluates to non-zero return tval, fval otherwise, e.g., If(1=2,3,4)=4, If(1,5,10)=5.

Max(x1,x2,...)
Returns the largest of the arguments xi. Examples:Max(1,2,3,4,5)=5Max(-3,2,0,2,1)=2

Min(x1,x2,...)
Returns the smallest of the arguments xi. Examples:Min(1,2,3,4,5)=1
Min(-3,2,0,2,1)=-3

Or(b1,b2,...)
Returns the logical disjunction of the arguments, which are all interpreted as Boolean expressions. If any of theexpressions evaluates to a non-zero value, Or returns 1. For example, Or(1=1,0,0)=1, Or(1=0,0,0)=0.

Switch(x,a1,b1,a2,b2,...,[def])

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

69

Using SMILE Wrappers
If x=a1, return b1, if x=a2, return b2, when x is not equal to any of as, return def (default value), which is an
optional argument. When x is not equal to any of as and no def is defined, return 0. Examples:
Switch(3,1,111,2,222,3,333,4,444,5,555,999)=333
Switch(8,1,111,2,222,3,333,4,444,5,555,999)=999
Switch(8,1,111,2,222,3,333,4,444,5,555)=0

Xor(b1,b2,...)
Returns logical exclusive OR of all arguments, which are all interpreted as Boolean expressions. Xor returns a 1
if the number of logical expressions that evaluate to non-zero is odd and a 0 otherwise.
Examples:Xor(0,1,2,-3,0)=1
Xor(1,1,0,2,2)=0
Xor(-5,1,1,-2,2)=1

6.15.9 Custom Functions
It is possible to extend the available function set by calling Network.setExtFunctions. The functions aredefined at the network level and stored within XDSL file. setExtFunctions requires an array of strings with thetextual definition of functions. Each function definition has the following form:
function-name([parameter1[,parameter2...]]) = expression
For example, a function adding two numbers can be defined by the following string:add(a,b)=a+b
Recursion is not allowed, but it is possible to use the preceding custom functions in the function expression, forexample:dbl(x)=x+xquad(x)=2*dbl(x)Reverse ordering of dbl and quad in the vector passed to setExtFunctions will result in a parser error for
quad, because the dbl function is not defined when quad is parsed.
Each parameter specified on the LHS of the equal sign must be used in the function body on the RHS. The
following is an incorrect custom function definition, because parameter c is not used:
myFunc(a,b,c)=sin(a)+cos(b)Custom function can be defined as a constant. In such case the function has no parameters, but its definition andfunction calls in node equations still require parenthesis:
gEarth()=9.80665
Random number generators can be used in function definitions:
StdNormal()=Normal(0,1)
TwoStep(lo,hi)=Steps(lo,(lo+hi)/2,hi,1,2)
To retrieve the custom functions defined for the network, call Network.getExtFunctions. By default, there areno custom functions. Each call to SetExtFunctions sets up a complete set of custom functions. To add newfunctions to the existing set, call getExtFunctions first, then add new function definition to the output vector
before calling setExtFunctions.

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

70

Using SMILE Wrappers
6.16 Diagnosis
Diagnosis is one of the most successful applications of Bayesian networks. The ability of probabilistic knowledge
representation techniques to perform a mixture of both predictive and diagnostic inference makes it very
suitable for diagnosis. Bayesian networks can perform fusion of observations such as patient (or equipment)
history and risk factors with symptoms and test results.
A Bayesian network built with SMILE represents various components of a system, possible faulty behaviors
produced by the system (symptoms), along with results of possible diagnostic tests. The model essentially
captures how possible defects of the system (whether it is a natural system, such as human body, or a humanmade device, such as a car, an airplane, or a copier) can manifest themselves by error messages, symptoms, andtest results.
To work with diagnosis, some nodes in the network should be designated as diagnostic faults, and some othernodes should become diagnostic observations. It is also possible to provide observation costs. The output of
SMILE diagnostic algorithms is two-fold: (1) the posterior marginal probability of diagnostic faults, and (2)ranking of possible observations from the most to the least informative from the point of view of the current
diagnostic focus. Depending on the selected diagnostic algorithm, the ranking is based on one of the following
measures:· an information-theoretic measure known as cross-entropy and expresses, for each observation node X

individually, the expected reduction in entropy of the probability distribution over the specified subset of fault
states after observing X. Cross-entropy is a utility-free measure of value of information and it gives a good idea
about the value of the observations for diagnosing the disorder in question.
· a distance between two vectors representing the probabilities of pursued faults. SMILE can use multiple

distance definitions, including Euclidean, cosine or cityblock.
Tutorial 9 153 uses a HeparII diagnostic model to show the diagnostic results for single- and multi-fault
diagnosis.

6.16.1 Diagnostic roles
Network.setNodeDiagType allows for setting for each node one of three possible diagnostic roles:· fault nodes, which require at least one fault state. During a diagnostic session, SMILE calculates theprobabilities of the fault states after a change in the set of observations. To set a state of a diagnostic fault node
to be a fault, use Network.setFaultOutcome.
· observation nodes, which can be instantiated (set to specified evidence) during a diagnostic session.

Observations can have default outcomes, which are instantiated automatically when a diagnostic session
begins. To set the default outcome, call Network.setDefaultOutcome. To mark that the observation node
should be included in the ranked list of observations, use Network.setRanked. To make the observation
mandatory, use Network.setMandatory. The diagnostic session will not rank observations until all
mandatory observations have been instantiated.
· auxiliary nodes, which are neither observation nor a fault. The auxiliary role is a default when node is created.

When a network object is saved to a .xdsl file, the diagnostic roles of nodes are preserved.

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

71

Using SMILE Wrappers
6.16.2 Observation cost
Observing the value of a variable is often associated with a cost. For example, in order to observe the platelet
count, one has to draw a blood sample and subject it to examination by professionals. Measuring thetemperature of an air conditioner exhaust unit requires a technician's time. In order to perform an optimal
diagnosis, one has to take into account the value of information along with the cost of obtaining it. For any
diagnostic observation node, we can specify the cost of observing its value by calling the Network.setNodeCost
method and read the cost by calling Network.getNodeCost.
The cost of performing a test can be expressed on some scale, like currency or time in minutes. It is also possible
to specify negative number as cost. Negative cost indicate that tests so inexpensive that they should always be
performed, for example checking the car model when diagnosing a car.
When observing the state of a node is independent of whether other nodes are observed or not, the cost is just asingle number. Sometimes, however, the cost of observing a variable is not independent of observing othervariables. For example, once a blood sample is drawn, performing additional tests on the sample is cheaper thanperforming these tests when no blood sample is available. The cost of measuring some parameter of alocomotive engine depends on whether the locomotive is in the shop or in the field. It may be much lower when
the locomotive is in the shop. Taking off a locomotive cover may take a few hours but once it is removed, many
tests become inexpensive. To represent such cases, use Network.addCostArc. This causes an observation cost
arc to be added to the network. The observation cost arcs are not constrained by 'normal' arcs. When diagnostic
observation has incoming cost arcs, its observation cost is a multi-dimensional matrix, just like conditional
probability table, but with the last dimension size equal to one.
To remove a cost arc, call Network.removeCostArc. Network.getCostParents/getCostParentIds andNetwork.getCostChildren/getCostChildrenIds can be used to retrieve the information about cost parents
and children.

6.16.3 Diagnostic session
With diagnostic attributes, such as node roles and (optionally) observation costs defined, we can start a
diagnostic session in order to obtain a ranked observation list. The diagnostic session is represented by
DiagNetwork object. Its constructor requires a reference to the Network object, where diagnostic information isdefined. The session object provides an API to pursue faults, instantiate observations, and retrieve the statistics
for observations. During the diagnostic session, your program should use the DiagNetwork instance to read
information from the network.
Java:
DiagNetwork diagNet = new DiagNetwork(net);
Python:diagnet = pysmile.DiagNetwork(net)R:
diagNet <- DiagNetwork(net)C#:
DiagNetwork diagNet = new DiagNetwork(net);
The diagnostic session instantiates the mandatory observation during its initialization. To obtain a ranking ofuninstantiated observations, we need to calculate their diagnostic value, which is a dynamic measure, depending

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

72

Using SMILE Wrappers
on the already instantiated observations and the fault or faults selected as the focus of reasoning (these faults arecalled "pursued faults"). By default, the pursued fault is the one which is most likely.
From the diagnostic session point of view, the fault is a node/state pair (because one fault node can have morethan one faulty state). The node/state pairs are referenced by their indices. Do not use fault node handles
directly when setting the pursued fault. To convert the node handle/state pair to fault index, use
DiagNetwork.getFaultIndex. To change the pursued fault, use DiagNetwork.setPursuedFault, or
setPursuedFaults if more than one fault is to be pursued.
During the diagnostic session, the observation nodes can be set to evidence with calls to
DiagNetwork.instantiateObservation. Do not call Network.setEvidence directly during thediagnostic session, as it will make the internal session state inconsistent. Instantiated observation can bereset to unobserved with Network.releaseObservation, which calls Network.clearEvidence internally.
After selecting the pursued faults and instantiating known observations, the application can call
DiagNetwork.update to obtain the DiagResults object, which contains two arrays:· DiagResults.faults contains an entry for each fault node in the network. Each entry includes theprobability of the fault state in its probability member. The array is sorted by fault probability in thedescending order.
· DiagResults.observations contains an entry for each uninstantiated observation node in the network.

Each entry includes the diagnostic measure for the observation in its measure member, and the infoGain
member contains effective measure combined with the observation cost. If there's no cost defined, infoGain
is equal to measure. The array is sorted by the value of infoGain in the descending order.
The typical diagnostic program is interactive, starting with single pursued fault and no instantiated observations
other than the ones with default value. The application user changes the pursued faults, and instantiates
observations taking into account the diagnostic values of the observations displayed in the UI.

6.16.4 Distance and entropy-based measures
The diagnostic measures calculated for observations depend on the pursued fault set. The pursued faults define
the focus of reasoning, and the changes in their probabilities are the inputs to the measure algorithms. To selectthe algorithm, use DiagNetwork.setSingleFaultAlgorithm and setMultiFaultAlgorithm. The output of
the algorithm is a single number for each uninstantiated observation.
The available algorithms for single fault diagnosis are:
· Max probability change (default): for each observation, the maximum change is taken over the absolutevalues of the change in probability of the pursued fault. Note that this is a signed measure, meaning that somevalues may be negative (when the largest magnitude of fault probability change represents the probability
decrease).
· Cross-entropy: the insight from the Max probability change is limited in the sense of not telling us a key pieceof information how likely each of the changes happens. For example, a positive test result for cancer will makea huge change it the probability of cancer. However, the probability of seeing a positive test may be very small
in a generally healthy person. So, effectively the expected amount of diagnostic information from performing
this test is rather small. Cross-entropy is an information-theoretic measure that takes into account both theamount of information flowing from observing individual states of an observation variable and theprobabilities of observing this states. A high cross-entropy indicates a high expected contribution of observing
a variable to the probability of the pursued fault. Cross-entropy is unsigned.

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

73

Using SMILE Wrappers· Normalized cross-entropy: the cross entropy divided by the current value of the entropy of the pursued fault

node
For multi-fault diagnosis, the following algorithms are available:· Max probability change (default): the value of the measure is the maximum change of probability over all

pursued faults and outcomes of the observation. This is a signed measure.
· Euclidean distance, or L2 Norm: calculates the distance between two vectors in Euclidean space, where thevector coordinates are the probabilities of the pursued faults before and after the observation. The distance isnormalized to ensure that a change from impossible (all coordinates are zero) to certain (all coordinates areones) is equal to 1.0. For each observation, the selected value is the greatest distance over all observation
outcomes. The larger the distance, the larger the impact of the observation.
· Cityblock distance: as above, but using cityblock metric· Averaged L2 and cityblock distance· Cosine distance, or cosine similarity: calculated between two fault probability vectors. Since vector

coordinates are probabilities, and therefore non-negative, this measure will always produce non-negativevalues (despite general cosine similarity range between -1 and 1).
· A family of six entropy-based measures, which require calculation of the joint probability distribution over all

pursued faults, which is computationally prohibitive, it is necessary to use approximations of the joint
probability distribution. The approximations are based on two strong assumptions about dependencies among
them: (1) complete independence (this is taken by the first group of approaches) and (2) complete dependence(this is taken by the second group of approaches). Each of the two extremes is divided into three groups: (1) At
Least One, (2) Only One, and (3) All. These refer to different partitioning of the combinations of diseases incross-entropy calculation.
· Two marginal probability-based measures, which are much faster than the independence/dependence-based

joint probability distribution approaches but it is not as accurate because they make a stronger assumption
about the joint probability distribution. Entropy calculations in this approach are based purely on themarginal probabilities of the pursued faults. The two algorithms that use the Marginal Probability Approachdiffer essentially in the function that they use to select the tests to perform. Both functions are scaled so thatthey return values between 0 and 1. Entropy/Marginal 1 uses a function without the support for maximum
distance and its minimum is reached when all probabilities of the faults are equal to 0.5. Entropy/Marginal 2
uses a function that has support for maximum distance and is continuous in the domain [0,1].

The numeric identifiers of the algorithms are defined as follows:
Java:
SingleFaultAlgorithmType and MultiFaultAlgorithmType are static classes nested in DiagNetwork.
SingleFaultAlgorithmType.PROB_CHANGE
SingleFaultAlgorithmType.CROSSENTROPY
SingleFaultAlgorithmType.NORM_CROSSENTROPY
MultiFaultAlgorithmType.MAX_PROB_CHANGE
MultiFaultAlgorithmType.L2_NORMALIZED_DISTANCE
MultiFaultAlgorithmType.CITYBLOCK_DISTANCE
MultiFaultAlgorithmType.AVG_L2_CITY_DISTANCE
MultiFaultAlgorithmType.COSINE_DISTANCE

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

74

Using SMILE Wrappers
MultiFaultAlgorithmType.INDEPENDENCE_AT_LEAST_ONE
MultiFaultAlgorithmType.INDEPENDENCE_ONLY_ONE
MultiFaultAlgorithmType.INDEPENDENCE_ONLY_ALL
MultiFaultAlgorithmType.DEPENDENCE_AT_LEAST_ONE
MultiFaultAlgorithmType.DEPENDENCE_ONLY_ONE
MultiFaultAlgorithmType.DEPENDENCE_ONLY_ALL
MultiFaultAlgorithmType.MARGINAL_1
MultiFaultAlgorithmType.MARGINAL_2
Python:SingleFaultAlgorithmType and MultiFaultAlgorithmType are enums.
SingleFaultAlgorithmType.PROB_CHANGE
SingleFaultAlgorithmType.CROSSENTROPY
SingleFaultAlgorithmType.NORM_CROSSENTROPY
MultiFaultAlgorithmType.MAX_PROB_CHANGE
MultiFaultAlgorithmType.L2_NORMALIZED_DISTANCE
MultiFaultAlgorithmType.CITYBLOCK_DISTANCE
MultiFaultAlgorithmType.AVG_L2_CITY_DISTANCE
MultiFaultAlgorithmType.COSINE_DISTANCE
MultiFaultAlgorithmType.INDEPENDENCE_AT_LEAST_ONE
MultiFaultAlgorithmType.INDEPENDENCE_ONLY_ONE
MultiFaultAlgorithmType.INDEPENDENCE_ONLY_ALL
MultiFaultAlgorithmType.DEPENDENCE_AT_LEAST_ONE
MultiFaultAlgorithmType.DEPENDENCE_ONLY_ONE
MultiFaultAlgorithmType.DEPENDENCE_ONLY_ALL
MultiFaultAlgorithmType.MARGINAL_1
MultiFaultAlgorithmType.MARGINAL_2
R:
SingleFaultAlgorithmType$PROB_CHANGE
SingleFaultAlgorithmType$CROSSENTROPY
SingleFaultAlgorithmType$NORM_CROSSENTROPY
MultiFaultAlgorithmType$MAX_PROB_CHANGE
MultiFaultAlgorithmType$L2_NORMALIZED_DISTANCE
MultiFaultAlgorithmType$CITYBLOCK_DISTANCE
MultiFaultAlgorithmType$AVG_L2_CITY_DISTANCE
MultiFaultAlgorithmType$COSINE_DISTANCE
MultiFaultAlgorithmType$INDEPENDENCE_AT_LEAST_ONE
MultiFaultAlgorithmType$INDEPENDENCE_ONLY_ONE
MultiFaultAlgorithmType$INDEPENDENCE_ONLY_ALL
MultiFaultAlgorithmType$DEPENDENCE_AT_LEAST_ONE
MultiFaultAlgorithmType$DEPENDENCE_ONLY_ONE
MultiFaultAlgorithmType$DEPENDENCE_ONLY_ALL
MultiFaultAlgorithmType$MARGINAL_1
MultiFaultAlgorithmType$MARGINAL_2
C#:
SingleFaultAlgorithmType and MultiFaultAlgorithmType are enum classes nested in DiagNetwork.
SingleFaultAlgorithmType.ProbChange
SingleFaultAlgorithmType.Crossentropy

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

75

Using SMILE Wrappers
SingleFaultAlgorithmType.NormCrossentropy
MultiFaultAlgorithmType.MaxProbChange
MultiFaultAlgorithmType.L2NormalizedDistanceMultiFaultAlgorithmType.CityblockDistanceMultiFaultAlgorithmType.AvgL2CityDistanceMultiFaultAlgorithmType.CosineDistanceMultiFaultAlgorithmType.IndependenceAtLeastOne
MultiFaultAlgorithmType.IndependenceOnlyOne
MultiFaultAlgorithmType.IndependenceOnlyAllMultiFaultAlgorithmType.DependenceAtLeastOne
MultiFaultAlgorithmType.DependenceOnlyOne
MultiFaultAlgorithmType.DependenceOnlyAllMultiFaultAlgorithmType.Marginal1
MultiFaultAlgorithmType.Marginal2

6.17 Learning
Learning in SMILE can perform two tasks:· structure learning: create a new network from a dataset· parameter learning: refine parameters (CPTs) in an existing network

SMILE also supports network validation, which is frequently used after learning to evaluate the results.

6.17.1 Learning network structure
The following classes can be used to learn Network from DataSet:· BayesianSearch: Bayesian Search, a hill climbing procedure guided by scoring heuristic with random restarts· NaiveBayes: Naive Bayes· TAN: Tree Augmented Naive Bayes, semi-naive method based on the Bayesian Search approach· ABN: Augmented Naive Bayes, another semi-naive method based on the Bayesian Search approach

In the simplest scenario, just create the object representing learning algorithm and call its learn method:Java:
DataSet ds = new DataSet();ds.readFile("mydatafile.txt");
BayesianSearch baySearch = new BayesianSearch();Network net = baySearch.learn(ds);Python:ds = pysmile.learning.DataSet()
ds.read_file("mydatafile.txt")
baySearch = pysmile.learning.BayesianSearch()

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

76

Using SMILE Wrappers
net = baySearch.learn(ds)R:
ds <- DataSet()
ds$readFile("mydatafile.txt")
baySearch <- BayesianSearch()
net <- baySearch$learn(ds)C#:
DataSet ds = new DataSet();ds.ReadFile("mydatafile.txt");
BayesianSearch baySearch = new BayesianSearch();Network net = baySearch.Learn(ds);Note that every variable in the dataset takes part in the learning process. After learning the structure, each of thealgorithms listed above performs parameter learning with EM, so the output network has nodes with parameters
based on the data.
The code example above used the default settings for Bayesian Search. To tweak the learning process, you canchange some control options in the learning object before calling its learn method. The example below sets thenumber of iterations and maximum number of parents:
Java:
BayesianSearch baySearch = new BayesianSearch();baySearch.setIterationCount(10);baySearch.setMaxParents(4);Network net = baySearch.learn(ds);Python:baySearch = pysmile.learning.BayesianSearch()
baySearch.set_iteration_count(10)baySearch.set_max_parents(4)net = baySearch.learn(ds)R:
baySearch <- BayesianSearch()
baySearch$setIterationCount(10)baySearch$setMaxParents(4)net <- baySearch$learn(ds)C#:BayesianSearch baySearch = new BayesianSearch();baySearch.IterationCount = 10;
baySearch.MaxParents = 4;
Network net = baySearch.Learn(ds);
For NaiveBayes and its semi-naive derivatives (TAN and ABN) it is required to specify a class variable identifier(a string value) with a call to setClassVariableId before invoking learn. This identifier has to match one of
the columns in the DataSet objects passed to learn.
SMILE also contains the PC class, which implements the PC structure learning algorithm (algorithm name is anacronym derived from its inventors' names). This algorithm uses DataSet as data source, but instead of
Network learns the Pattern object, which is a graph with directed and undirected edges, which is notguaranteed to be acyclic.

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

77

Using SMILE Wrappers
BayesianSearch and PC algorithms can use background knowledge, provided by the caller. The backgroundknowledge influences the learned structure by:· forcing arcs between specified variables· forbidding arcs between specified variables· ordering specified variables by temporal tiers: in the resulting structure, there will be no arcs from nodes inhigher tiers to nodes in lower tiers
To specify the background knowledge, use BayesianSearch.setBkKnowledge or PC.setBkKnowledge
methods.
Tutorial 10 169 contains a program which performs structure learning using Bayesian Search, Tree Augmented
Naive Bayes and PC.

6.17.2 Learning network parameters
To learn the parameters in the existing Network object, you can use the EM algorithm implemented in EM class.
As with structure learning, the data comes in DataSet object. However, the network and the data must be
matched to ensure that learning algorithm knows the relationship between the dataset variables and network
nodes. If the variables and nodes have identical identifiers, you can use the DataSet.matchNetwork method:Java:
DataSet ds = new DataSet();Network net = new Network();// load network and data here
DataMatch[] matching = ds.matchNetwork(net);
em = new EM();em.learn(ds, net, matching);
Python:ds = pysmile.learning.DataSet()
net = pysmile.Network()# load network and data here
matching = ds.match_network(net)em = pysmile.learning.EM()
em.learn(ds, net, matching)R:
ds <- DataSet()
net <- Network()# load network and data here
matching <- ds$matchNetwork(net)em <- EM()
em$learn(ds, net, matching)C#:
DataSet ds = new DataSet();Network net = new Network();// load network and data here
DataMatch[] matching = ds.MatchNetwork(net);
em = new EM();
em.Learn(ds, net, matching);

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

78

Using SMILE Wrappers
If your network and data cannot be automatically matched with DataSet.matchNetwork, you can build thearray of DataMatch objects in your own code. DataMatch has node and column fields representing node handle
and variable index, respectively. For each node/variable pair you need one element of the array.
The final section of Tutorial 10 169 is using EM parameter learning on a structure created by the PC algorithm.

6.17.3 Validation
To evaluate the predictive quality of your network you can use the Validator class.
The Validator constructor requires references to DataSet and Network objects to be specified. To properly
match the network and data the constructor also requires the array of DataMatch objects (as did EM.learn
method).
After the validator object is constructed, you need to specify which nodes in the network are considered classnodes by calling Validator.addClassNode method. Validation requires at least one class node.
For each record in the dataset during the validation, the variables matched to non-class nodes are used to set theevidence. The posterior probabilities are then calculated and for each class node the outcome with the highest
probability is selected as a predicted outcome. The prediction is compared with an outcome in the dataset
variable associated with the class node. The number of matches and calculated posteriors are used to obtain theaccuracy, confusion matrix, ROC and calibration curves.
Validation can be either performed without parameter learning using Validator.test method, or withparameter learning using Validator::kFold and leaveOneOut methods. K-fold crossvalidation divides thedataset into K parts of equal size, trains the network on K-1 parts, and tests it on the last, Kth part. The process isrepeated K times, with a different part of the data being selected for testing. Leave-one-out is an extreme case of
K-fold, in which K is equal to the number of records in the data set.
The example below performs K-fold crossvalidation with 5 folds using one class node. The accuracy is obtained
for the outcome with the index zero (that is, the first outcome of the node).
Java:
DataSet ds = new DataSet();Network net = new Network();// load network and data here
DataMatch[] matching = ds.matchNetwork();
Validator validator = new Validator(ds, net, matching);
int classNodehandle = net.getNode("someNodeId");
validator.addClassNode(classNodeHandle);
EM em = new EM();// optionally tweak EM options here
validator.kFold(em, 5);double acc = validator.getAccuracy(classNodeHandle, 0);
Python:ds = pysmile.learning.DataSet()
net = pysmile.Network()# load network and data here
matching = ds.match_network(net)validator = pysmile.learning.Validator(ds, net, matching)classNodehandle = net.getNode("someNodeId")

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

Using SMILE Wrappers

79

validator.addClassNode(classNodeHandle)
em = pysmile.learning.EM()# optionally tweak EM options here
validator.k_fold(em, 5)
acc = validator.get_accuracy(classNodeHandle, 0)R:
ds <- DataSet()
net <- Network()# load network and data here
matching <- ds$matchNetwork()
validator <- Validator(ds, net, matching)classNodehandle <- net$getNode("someNodeId")
validator$addClassNode(classNodeHandle)
em <- EM()
# optionally tweak EM options here
validator$kFold(em, 5)
acc <- validator$getAccuracy(classNodeHandle, 0)C#:
DataSet ds = new DataSet();Network net = new Network();// load network and data here
DataMatch[] matching = ds.MatchNetwork();
Validator validator = new Validator(ds, net, matching);
int classNodehandle = net.GetNode("someNodeId");
validator.AddClassNode(classNodeHandle);
EM em = new EM();// optionally tweak EM options here
validator.KFold(em, 5);double acc = validator.GetAccuracy(classNodeHandle, 0);

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

This page is intentionally left blank.

Tutorials

82

Tutorials
7

Tutorials

The complete, functionally equivalent source code for all the tutorials in Java, Python, R and C# is provided laterin this chapter. The multi-line code snippets in the tutorial descriptions preceeding the source code are also
given in Java, Python and C#. The inline examples in text paragraphs are using Java syntax (likeNetwork.updateBeliefs) for brevity.
You can also download tutorial sources from our documentation site at https://support.bayesfusion.com/docs

7.1

Tutorial 1: Creating a Bayesian Network

Consider a slight twist on the problem described in the Hello, SMILE Wrapper! 22 section of this manual.
The twist will include adding an additional variable State of the economy (with the identifier Economy) withthree outcomes (Up, Flat, and Down) modeling the developments in the economy. These developments are
relevant to the decision, as they are impacting expert predictions. When the economy is heading Up, our expert
makes more optimistic predictions, when it is heading Down, the expert makes more pessimistic predictions forthe same venture. This is reflected by a directed arc from the node State of the economy to the node Expert
forecast. State of the economy also impacts the probability of venture being successful, which we model byadding another arc.

We will show how to create this model and how to save it to disk. In subsequent tutorials, we will show how toenter observations (evidence), how to perform inference, and how to retrieve the calculation results.
We start by declaring our network variable. The three nodes in the network are subsequently created by calling ahelper method createCptNode.
Java:
Network net = new Network();
int e = createCptNode(net,"Economy", "State of the economy",
new String[] {"Up","Flat","Down"},
160, 40);
int s = createCptNode(net,"Success", "Success of the venture",
new String[] {"Success","Failure"},60, 40);int f = createCptNode(net,"Forecast", "Expert forecast",
new String[] {"Good","Moderate","Poor"},110, 140);

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

Tutorials

83

Python:net = pysmile.Network()e = self.create_cpt_node(net,"Economy", "State of the economy",["Up","Flat","Down"],
160, 40)s = self.create_cpt_node(net,"Success", "Success of the venture",["Success","Failure"],60, 40)f = self.create_cpt_node(net,"Forecast", "Expert forecast",["Good","Moderate","Poor"],110, 140)
C#:
Network net = new Network();
int e = CreateCptNode(net,"Economy", "State of the economy",
new String[] { "Up", "Flat", "Down" },160, 40);
int s = CreateCptNode(net,"Success", "Success of the venture",
new String[] { "Success", "Failure" },60, 40);int f = CreateCptNode(net,"Forecast", "Expert forecast",
new String[] { "Good", "Moderate", "Poor" },110, 140);
Before connecting the nodes with arcs, let’s have a look at the createCptNode method.
Java:
private static int createCptNode(
Network net, String id, String name,
String[] outcomes, int xPos, int yPos) {int handle = net.addNode(Network.NodeType.CPT, id);
net.setNodeName(handle, name);
net.setNodePosition(handle, xPos, yPos, 85, 55);
int initialOutcomeCount = net.getOutcomeCount(handle);for (int i = 0; i < initialOutcomeCount; i ++) {net.setOutcomeId(handle, i, outcomes[i]);}for (int i = initialOutcomeCount; i < outcomes.length; i ++) {net.addOutcome(handle, outcomes[i]);}return handle;}
Python:def create_cpt_node(self, net, id, name, outcomes, x_pos, y_pos):handle = net.add_node(pysmile.NodeType.CPT, id)
net.set_node_name(handle, name)net.set_node_position(handle, x_pos, y_pos, 85, 55)
initial_outcome_count = net.get_outcome_count(handle)

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

84

Tutorials
for i in range(0, initial_outcome_count):
net.set_outcome_id(handle, i, outcomes[i])for i in range(initial_outcome_count, len(outcomes)):
net.add_outcome(handle, outcomes[i])return handle
C#:
private static int CreateCptNode(
Network net, String id, String name,
String[] outcomes, int xPos, int yPos){
int handle = net.AddNode(Network.NodeType.Cpt, id);
net.SetNodeName(handle, name);
net.SetNodePosition(handle, xPos, yPos, 85, 55);
int initialOutcomeCount = net.GetOutcomeCount(handle);for (int i = 0; i < initialOutcomeCount; i++){net.SetOutcomeId(handle, i, outcomes[i]);}for (int i = initialOutcomeCount; i < outcomes.Length; i++){net.AddOutcome(handle, outcomes[i]);}return handle;}
The function creates a CPT node with specified identifier, name, outcomes and position on the screen. CPT
nodes are created with two outcomes named State0 and State1. To change the number of node outcomes andrename them, we will use two loops - the first renames the default outcomes and the second adds new outcomes.
We can now add arcs linking the nodes. Note that we can use either node handles or node identifiers. In thissimple program, we use both forms just to showcase this feature.
Java:
net.addArc(e, s);net.addArc(s, f);net.addArc("Economy", "Forecast");
Python:net.add_arc(e, s)
net.add_arc(s, f)
net.add_arc("Economy", "Forecast")
C#:
net.AddArc(e, s);net.AddArc(s, f);net.AddArc("Economy", "Forecast");Next step is to initialize the conditional probability tables of the nodes. See the Multidimensional arrays 36
section in this manual for the description of the CPT memory layout. For each of three nodes we call the
Network.setNodeDefinition method. Here's how we set the probabilities of the Success node - note that thecomments for each probability show the combination of outcomes it is defined for.
Java:
double[] successDef = new double[] {0.3, // P(Success=S|Economy=U)

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

85

Tutorials
0.7, // P(Success=F|Economy=U)
0.2, // P(Success=S|Economy=F)
0.8, // P(Success=F|Economy=F)
0.1, // P(Success=S|Economy=D)
0.9 // P(Success=F|Economy=D)};
net.setNodeDefinition(s, successDef);
Python:successDef = [0.3, # P(Success=S|Economy=U)
0.7, # P(Success=F|Economy=U)
0.2, # P(Success=S|Economy=F)
0.8, # P(Success=F|Economy=F)
0.1, # P(Success=S|Economy=D)
0.9 # P(Success=F|Economy=D)]
net.set_node_definition(s, successDef)
C#:double[] successDef = new double[]{
0.3, // P(Success=S|Economy=U)
0.7, // P(Success=F|Economy=U)
0.2, // P(Success=S|Economy=F)
0.8, // P(Success=F|Economy=F)
0.1, // P(Success=S|Economy=D)
0.9 // P(Success=F|Economy=D)};
net.SetNodeDefinition(s, successDef);
With CPTs initialized our network is complete. We write its contents to the tutorial1.xdsl file. Tutorial 2 will load
this file and perform the inference. The split between tutorials is artificial, your program can use networks rightafter its creation without the need to write/read from the file system.
Java:
net.writeFile("tutorial1.xdsl");Python:net.write_file("tutorial1.xdsl");
C#:
net.WriteFile("tutorial1.xdsl");

7.1.1

Tutorial1.java

package tutorials;
import smile.*;// Tutorial1 creates a simple network with three nodes,// then writes its content as XDSL file to disk.
public class Tutorial1 {public static void run() {System.out.println("Starting Tutorial1...");

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

Tutorials

86

Network net = new Network();
int e = createCptNode(net,"Economy", "State of the economy",
new String[] {"Up","Flat","Down"},
160, 40);
int s = createCptNode(net,"Success", "Success of the venture",
new String[] {"Success","Failure"},60, 40);int f = createCptNode(net,"Forecast", "Expert forecast",
new String[] {"Good","Moderate","Poor"},110, 140);
net.addArc(e, s);net.addArc(s, f);// we can also use node identifiers when creating arcs
net.addArc("Economy", "Forecast");double[] economyDef = {0.2, // P(Economy=U)
0.7, // P(Economy=F)
0.1 // P(Economy=D)};
net.setNodeDefinition(e, economyDef);double[] successDef = new double[] {0.3, // P(Success=S|Economy=U)
0.7, // P(Success=F|Economy=U)
0.2, // P(Success=S|Economy=F)
0.8, // P(Success=F|Economy=F)
0.1, // P(Success=S|Economy=D)
0.9 // P(Success=F|Economy=D)};
net.setNodeDefinition(s, successDef);double[] forecastDef = new double[] {0.70, // P(Forecast=G|Success=S,Economy=U)
0.29, // P(Forecast=M|Success=S,Economy=U)
0.01, // P(Forecast=P|Success=S,Economy=U)
0.65, // P(Forecast=G|Success=S,Economy=F)
0.30, // P(Forecast=M|Success=S,Economy=F)
0.05, // P(Forecast=P|Success=S,Economy=F)
0.60, // P(Forecast=G|Success=S,Economy=D)
0.30, // P(Forecast=M|Success=S,Economy=D)
0.10, // P(Forecast=P|Success=S,Economy=D)
0.15, // P(Forecast=G|Success=F,Economy=U)
0.30, // P(Forecast=M|Success=F,Economy=U)

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

87

Tutorials
0.55, // P(Forecast=P|Success=F,Economy=U)
0.10, // P(Forecast=G|Success=F,Economy=F)
0.30, // P(Forecast=M|Success=F,Economy=F)
0.60, // P(Forecast=P|Success=F,Economy=F)
0.05, // P(Forecast=G|Success=F,Economy=D)
0.25, // P(Forecast=G|Success=F,Economy=D)
0.70 // P(Forecast=G|Success=F,Economy=D)};
net.setNodeDefinition(f, forecastDef);
net.writeFile("tutorial1.xdsl");
System.out.println("Tutorial1 complete: Network written to tutorial1.xdsl");}private static int createCptNode(
Network net, String id, String name,
String[] outcomes, int xPos, int yPos) {int handle = net.addNode(Network.NodeType.CPT, id);
net.setNodeName(handle, name);
net.setNodePosition(handle, xPos, yPos, 85, 55);
int initialOutcomeCount = net.getOutcomeCount(handle);for (int i = 0; i < initialOutcomeCount; i ++) {net.setOutcomeId(handle, i, outcomes[i]);}for (int i = initialOutcomeCount; i < outcomes.length; i ++) {net.addOutcome(handle, outcomes[i]);}return handle;
}
}

7.1.2

Tutorial1.py

import pysmile# Tutorial1 creates a simple network with three nodes,# then writes its content as XDSL file to disk.
class Tutorial1:
def __init__(self):
print("Starting Tutorial1...")
net = pysmile.Network()e = self.create_cpt_node(net,"Economy", "State of the economy",["Up","Flat","Down"],
160, 40)

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

Tutorials

88s = self.create_cpt_node(net,"Success", "Success of the venture",["Success","Failure"],60, 40)f = self.create_cpt_node(net,"Forecast", "Expert forecast",["Good","Moderate","Poor"],110, 140)
net.add_arc(e, s)
net.add_arc(s, f)# we can also use node identifiers when creating arcs
net.add_arc("Economy", "Forecast");economyDef = [0.2, # P(Economy=U)
0.7, # P(Economy=F)
0.1 # P(Economy=D)]
net.set_node_definition(e, economyDef)
successDef = [0.3, # P(Success=S|Economy=U)
0.7, # P(Success=F|Economy=U)
0.2, # P(Success=S|Economy=F)
0.8, # P(Success=F|Economy=F)
0.1, # P(Success=S|Economy=D)
0.9 # P(Success=F|Economy=D)]
net.set_node_definition(s, successDef)
forecastDef = [0.70, # P(Forecast=G|Success=S,Economy=U)
0.29, # P(Forecast=M|Success=S,Economy=U)
0.01, # P(Forecast=P|Success=S,Economy=U)
0.65, # P(Forecast=G|Success=S,Economy=F)
0.30, # P(Forecast=M|Success=S,Economy=F)
0.05, # P(Forecast=P|Success=S,Economy=F)
0.60, # P(Forecast=G|Success=S,Economy=D)
0.30, # P(Forecast=M|Success=S,Economy=D)
0.10, # P(Forecast=P|Success=S,Economy=D)
0.15, # P(Forecast=G|Success=F,Economy=U)
0.30, # P(Forecast=M|Success=F,Economy=U)
0.55, # P(Forecast=P|Success=F,Economy=U)
0.10, # P(Forecast=G|Success=F,Economy=F)
0.30, # P(Forecast=M|Success=F,Economy=F)
0.60, # P(Forecast=P|Success=F,Economy=F)
0.05, # P(Forecast=G|Success=F,Economy=D)

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

89

Tutorials
0.25, # P(Forecast=G|Success=F,Economy=D)
0.70 # P(Forecast=G|Success=F,Economy=D)]
net.set_node_definition(f, forecastDef)
net.write_file("tutorial1.xdsl")
print("Tutorial1 complete: Network written to tutorial1.xdsl")
def create_cpt_node(self, net, id, name, outcomes, x_pos, y_pos):handle = net.add_node(pysmile.NodeType.CPT, id)
net.set_node_name(handle, name)net.set_node_position(handle, x_pos, y_pos, 85, 55)
initial_outcome_count = net.get_outcome_count(handle)
for i in range(0, initial_outcome_count):
net.set_outcome_id(handle, i, outcomes[i])for i in range(initial_outcome_count, len(outcomes)):
net.add_outcome(handle, outcomes[i])return handle

7.1.3

Tutorial1.R

library(rSMILE)
source("License.R")
createCptNode = function(net, id, name, outcomes, xPos, yPos) {handle <- net$addNode(net$NodeType$CPT, id)
net$setNodeName(handle, name)net$setNodePosition(handle, xPos, yPos, 85L, 55L)
outcomesCount <- length(outcomes)
initialOutcomeCount <- net$getOutcomeCount(handle)
i <- 0for (outcome in outcomes[1:initialOutcomeCount]) {net$setOutcomeId(handle, i, outcome)i <- i + 1}
if ((initialOutcomeCount + 1) <= outcomesCount) {for (outcome in outcomes[(initialOutcomeCount + 1):(outcomesCount)]) {net$addOutcome(handle, outcome)}}return(handle)}
cat("Starting Tutorial1...\n")
net <- Network()

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

90

Tutorials
e <- createCptNode(net, "Economy", "State of the economy",c("Up", "Flat", "Down"), 160L, 40L)
s <- createCptNode(net, "Success", "Success of the venture",c("Success","Failure"), 60L, 40L)
f <- createCptNode(net, "Forecast", "Expert forecast",c("Good","Moderate","Poor"), 110L, 140L)
net$addArc(e, s)
net$addArc(s, f)
net$addArc("Economy", "Forecast")
economyDef <- c(0.2, # P(Economy=U)
0.7, # P(Economy=F)
0.1 # P(Economy=D))
net$setNodeDefinition(e, economyDef)
successDef <- c(0.3, # P(Success=S|Economy=U)
0.7, # P(Success=F|Economy=U)
0.2, # P(Success=S|Economy=F)
0.8, # P(Success=F|Economy=F)
0.1, # P(Success=S|Economy=D)
0.9 # P(Success=F|Economy=D))
net$setNodeDefinition(s, successDef)
forecastDef <- c(0.70, # P(Forecast=G|Success=S,Economy=U)
0.29, # P(Forecast=M|Success=S,Economy=U)
0.01, # P(Forecast=P|Success=S,Economy=U)
0.65, # P(Forecast=G|Success=S,Economy=F)
0.30, # P(Forecast=M|Success=S,Economy=F)
0.05, # P(Forecast=P|Success=S,Economy=F)
0.60, # P(Forecast=G|Success=S,Economy=D)
0.30, # P(Forecast=M|Success=S,Economy=D)
0.10, # P(Forecast=P|Success=S,Economy=D)
0.15, # P(Forecast=G|Success=F,Economy=U)
0.30, # P(Forecast=M|Success=F,Economy=U)
0.55, # P(Forecast=P|Success=F,Economy=U)
0.10, # P(Forecast=G|Success=F,Economy=F)
0.30, # P(Forecast=M|Success=F,Economy=F)
0.60, # P(Forecast=P|Success=F,Economy=F)
0.05, # P(Forecast=G|Success=F,Economy=D)
0.25, # P(Forecast=G|Success=F,Economy=D)
0.70 # P(Forecast=G|Success=F,Economy=D))
net$setNodeDefinition(f, forecastDef)

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

Tutorials

91

net$writeFile("tutorial1.xdsl")
cat("Tutorial1 complete: Network written to tutorial1.xdsl\n")7.1.4

Tutorial1.cs

using System;
using Smile;// Tutorial1 creates a simple network with three nodes,// then writes its content as XDSL file to disk.
namespace SmileNetTutorial{class Tutorial1{
public static void Run(){
Console.WriteLine("Starting Tutorial1...");
Network net = new Network();
int e = CreateCptNode(net,"Economy", "State of the economy",
new String[] { "Up", "Flat", "Down" },160, 40);
int s = CreateCptNode(net,"Success", "Success of the venture",
new String[] { "Success", "Failure" },60, 40);int f = CreateCptNode(net,"Forecast", "Expert forecast",
new String[] { "Good", "Moderate", "Poor" },110, 140);
net.AddArc(e, s);net.AddArc(s, f);// we can also use node identifiers when creating arcs
net.AddArc("Economy", "Forecast");double[] economyDef ={0.2, // P(Economy=U)
0.7, // P(Economy=F)
0.1 // P(Economy=D)};
net.SetNodeDefinition(e, economyDef);double[] successDef = new double[]{
0.3, // P(Success=S|Economy=U)
0.7, // P(Success=F|Economy=U)
0.2, // P(Success=S|Economy=F)

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

92

Tutorials
0.8, // P(Success=F|Economy=F)
0.1, // P(Success=S|Economy=D)
0.9 // P(Success=F|Economy=D)};
net.SetNodeDefinition(s, successDef);double[] forecastDef = new double[]{
0.70, // P(Forecast=G|Success=S,Economy=U)
0.29, // P(Forecast=M|Success=S,Economy=U)
0.01, // P(Forecast=P|Success=S,Economy=U)
0.65, // P(Forecast=G|Success=S,Economy=F)
0.30, // P(Forecast=M|Success=S,Economy=F)
0.05, // P(Forecast=P|Success=S,Economy=F)
0.60, // P(Forecast=G|Success=S,Economy=D)
0.30, // P(Forecast=M|Success=S,Economy=D)
0.10, // P(Forecast=P|Success=S,Economy=D)
0.15, // P(Forecast=G|Success=F,Economy=U)
0.30, // P(Forecast=M|Success=F,Economy=U)
0.55, // P(Forecast=P|Success=F,Economy=U)
0.10, // P(Forecast=G|Success=F,Economy=F)
0.30, // P(Forecast=M|Success=F,Economy=F)
0.60, // P(Forecast=P|Success=F,Economy=F)
0.05, // P(Forecast=G|Success=F,Economy=D)
0.25, // P(Forecast=G|Success=F,Economy=D)
0.70 // P(Forecast=G|Success=F,Economy=D)};
net.SetNodeDefinition(f, forecastDef);
net.WriteFile("tutorial1.xdsl");Console.WriteLine("Tutorial1 complete: Network written to tutorial1.xdsl");}private static int CreateCptNode(
Network net, String id, String name,
String[] outcomes, int xPos, int yPos){
int handle = net.AddNode(Network.NodeType.Cpt, id);
net.SetNodeName(handle, name);
net.SetNodePosition(handle, xPos, yPos, 85, 55);
int initialOutcomeCount = net.GetOutcomeCount(handle);for (int i = 0; i < initialOutcomeCount; i++){net.SetOutcomeId(handle, i, outcomes[i]);}for (int i = initialOutcomeCount; i < outcomes.Length; i++)

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

93

Tutorials{
net.AddOutcome(handle, outcomes[i]);}return handle;
}
}
}

7.2

Tutorial 2: Inference with a Bayesian Network

Tutorial 2 begins with the model we have previously created. We will perform multiple calls to Bayesianinference algorithm through the Network.updateBeliefs, starting with network without any evidence. Aftereach updateBeliefs call the posterior probabilities of nodes will be displayed.
The model is loaded with the Network.readFile.
Java:
Java:
net.readFile("tutorial1.xdsl");Python:net.read_file("tutorial1.xdsl");
C#:
net.ReadFile("tutorial1.xdsl");
We update the probabilities and proceed to display them using the helper method printAllPosteriors
defined in this tutorial.
Java:
printf("Posteriors with no evidence set:\n");net.updateBeliefs();
printAllPosteriors(net);
Python:print("Posteriors with no evidence set:")net.update_beliefs()
self.print_all_posteriors(net)C#:
Console.WriteLine("Posteriors with no evidence set:");net.UpdateBeliefs();
PrintAllPosteriors(net);
printAllPosteriors displays posterior probabilities calculated by updateBeliefs for each node. To iterateover the nodes, Network.getFirstNode and getNextNode are used. In the body of the loop we call anotherhelper function, printPosteriors.
Java:for (int h = net.getFirstNode(); h >= 0; h = net.getNextNode(h)) {printPosteriors(net, h);}

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

94

Tutorials
Python:handle = net.get_first_node()while (handle >= 0):
self.print_posteriors(net, handle)
handle = net.get_next_node(handle)C#:for (int h = net.GetFirstNode(); h >= 0; h = net.GetNextNode(h)){
PrintPosteriors(net, h);}printPosteriors checks if node has evidence set by calling Network.isEvidence; if this is the case the nameof the evidence state is displayed. Please note that for demonstration purposes this tutorial calls
Network.getEvidence to obtain an integer representing the index of the evidence state, then converts theinteger index to outcome id with Network.getOutcomeId. The Network.getEvidenceId method combinesthese two actions into one call.
If node is does not have any evidence set, printPosteriors iterates over all states and displays the posterior
probability of each.
Back in the main function of the tutorial, we repeatedly call changeEvidenceAndUpdate helper function tochange evidence, update network and display posteriors. Note that we need to call different methods of the
Network class to set evidence to the specified outcome (setEvidence) and remove the evidence(clearEvidence).

7.2.1

Tutorial2.java

package tutorials;
import smile.*;// Tutorial2 loads the XDSL file created by Tutorial1,// then performs the series of inference calls,// changing evidence each time.
public class Tutorial2 {public static void run() {System.out.println("Starting Tutorial2...");
Network net = new Network();// load the network created by Tutorial1
net.readFile("tutorial1.xdsl");
System.out.println("Posteriors with no evidence set:");net.updateBeliefs();
printAllPosteriors(net);
System.out.println("Setting Forecast=Good.");changeEvidenceAndUpdate(net, "Forecast", "Good");
System.out.println("Adding Economy=Up.");changeEvidenceAndUpdate(net, "Economy", "Up");
System.out.println("Changing Forecast to Poor, keeping Economy=Up.");

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

95

Tutorials
changeEvidenceAndUpdate(net, "Forecast", "Poor");
System.out.println("Removing evidence from Economy, keeping Forecast=Poor.");changeEvidenceAndUpdate(net, "Economy", null);
System.out.println("Tutorial2 complete.");}private static void printPosteriors(Network net, int nodeHandle) {String nodeId = net.getNodeId(nodeHandle);
if (net.isEvidence(nodeHandle)) {System.out.printf("%s has evidence set (%s)\n",nodeId,net.getOutcomeId(nodeHandle, net.getEvidence(nodeHandle)));} else {double[] posteriors = net.getNodeValue(nodeHandle);for (int i = 0; i < posteriors.length; i ++) {System.out.printf("P(%s=%s)=%f\n",nodeId,net.getOutcomeId(nodeHandle, i),posteriors[i]);}}}private static void printAllPosteriors(Network net) {for (int h = net.getFirstNode(); h >= 0; h = net.getNextNode(h)) {printPosteriors(net, h);}System.out.println();}private static void changeEvidenceAndUpdate(
Network net, String nodeId, String outcomeId) {if (outcomeId != null) {net.setEvidence(nodeId, outcomeId);} else {net.clearEvidence(nodeId);}
net.updateBeliefs();
printAllPosteriors(net);
}
}

7.2.2

Tutorial2.py

import pysmile# Tutorial2 loads the XDSL file created by Tutorial1,# then performs the series of inference calls,# changing evidence each time.
class Tutorial2:
def __init__(self):

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

96

Tutorials
print("Starting Tutorial2...")
net = pysmile.Network()# load the network created by Tutorial1
net.read_file("tutorial1.xdsl")
print("Posteriors with no evidence set:")net.update_beliefs()
self.print_all_posteriors(net)
print("Setting Forecast=Good.")
self.change_evidence_and_update(net, "Forecast", "Good")
print("Adding Economy=Up.")
self.change_evidence_and_update(net, "Economy", "Up")print("Changing Forecast to Poor, keeping Economy=Up.")
self.change_evidence_and_update(net, "Forecast", "Poor")
print("Removing evidence from Economy, keeping Forecast=Poor.")self.change_evidence_and_update(net, "Economy", None)
print("Tutorial2 complete.")def print_posteriors(self, net, node_handle):
node_id = net.get_node_id(node_handle)
if net.is_evidence(node_handle):
print(f"{node_id} has evidence set ({net.get_evidence_id(node_handle)})")
else :
posteriors = net.get_node_value(node_handle)
for i in range(0, len(posteriors)):
print(f"P({node_id}={net.get_outcome_id(node_handle, i)})={posteriors[i]}")def print_all_posteriors(self, net):for handle in net.get_all_nodes():self.print_posteriors(net, handle)
def change_evidence_and_update(self, net, node_id, outcome_id):
if outcome_id is not None:
net.set_evidence(node_id, outcome_id)
else:
net.clear_evidence(node_id)
net.update_beliefs()
self.print_all_posteriors(net)
print()

7.2.3

Tutorial2.R

library(rSMILE)
source("License.R")
printPosteriors = function(net, nodeHandle) {nodeId <- net$getNodeId(nodeHandle)
if (net$isEvidence(nodeHandle)) {cat(sprintf("%s has evidence set (%s)\n",SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

Tutorials

97

nodeId,net$getOutcomeId(nodeHandle, net$getEvidence(nodeHandle))))} else {posteriors <- net$getNodeValue(nodeHandle)for (i in 0:(length(posteriors)-1)) {cat(sprintf("P(%s=%s)=%f\n",nodeId, net$getOutcomeId(nodeHandle, i),posteriors[i+1]))}}}
printAllPosteriors = function(net) {nodes <- net$getAllNodes()for (h in nodes) {printPosteriors(net, h)}cat("\n")}changeEvidenceAndUpdate = function(net, nodeId, outcomeId) {if (is.null(outcomeId)) {net$clearEvidence(nodeId)} else {net$setEvidence(nodeId, outcomeId)}
net$updateBeliefs()
printAllPosteriors(net)}cat("Starting Tutorial2...\n")
net <- Network()# load the network created by Tutorial1
net$readFile("tutorial1.xdsl")
cat("Posteriors with no evidence set:\n")
net$updateBeliefs()
printAllPosteriors(net)cat("Setting Forecast=Good.\n");changeEvidenceAndUpdate(net, "Forecast", "Good");cat("Adding Economy=Up.\n");changeEvidenceAndUpdate(net, "Economy", "Up");cat("Changing Forecast to Poor, keeping Economy=Up.\n");changeEvidenceAndUpdate(net, "Forecast", "Poor");cat("Removing evidence from Economy, keeping Forecast=Poor.\n");changeEvidenceAndUpdate(net, "Economy", NULL);
cat("Tutorial2 complete.\n");SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

98

Tutorials
7.2.4

Tutorial2.cs

using System;
using Smile;// Tutorial2 loads the XDSL file created by Tutorial1,// then performs the series of inference calls,// changing evidence each time.
namespace SmileNetTutorial{class Tutorial2{
public static void Run(){
Console.WriteLine("Starting Tutorial2...");
Network net = new Network();// load the network created by Tutorial1
net.ReadFile("tutorial1.xdsl");Console.WriteLine("Posteriors with no evidence set:");net.UpdateBeliefs();
PrintAllPosteriors(net);
Console.WriteLine("Setting Forecast=Good.");ChangeEvidenceAndUpdate(net, "Forecast", "Good");
Console.WriteLine("Adding Economy=Up.");ChangeEvidenceAndUpdate(net, "Economy", "Up");
Console.WriteLine("Changing Forecast to Poor, keeping Economy=Up.");ChangeEvidenceAndUpdate(net, "Forecast", "Poor");
Console.WriteLine("Removing evidence from Economy, keeping Forecast=Poor.");ChangeEvidenceAndUpdate(net, "Economy", null);Console.WriteLine("Tutorial2 complete.");}private static void PrintPosteriors(Network net, int nodeHandle){
String nodeId = net.GetNodeId(nodeHandle);
if (net.IsEvidence(nodeHandle)){
Console.WriteLine("{0} has evidence set ({1})",
nodeId,net.GetOutcomeId(nodeHandle, net.GetEvidence(nodeHandle)));}else{
double[] posteriors = net.GetNodeValue(nodeHandle);for (int i = 0; i < posteriors.Length; i++){Console.WriteLine("P({0}={1})={2}",SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

99

Tutorials
nodeId, net.GetOutcomeId(nodeHandle, i), posteriors[i]);}}}private static void PrintAllPosteriors(Network net){for (int h = net.GetFirstNode(); h >= 0; h = net.GetNextNode(h)){
PrintPosteriors(net, h);}Console.WriteLine();}private static void ChangeEvidenceAndUpdate(
Network net, String nodeId, String outcomeId){
if (outcomeId != null){
net.SetEvidence(nodeId, outcomeId);}
else{
net.ClearEvidence(nodeId);}
net.UpdateBeliefs();
PrintAllPosteriors(net);
}
}
}

7.3

Tutorial 3: Exploring the contents of a modelThis tutorial will not perform any calculations. Instead, we will display the information about the network
structure (nodes and arcs) and parameters (in this case, conditional probability tables).
We load the network created by Tutorial1 and for each node invoke the locally defined helper method
printNodeInfo. This is where real work is done. The first displayed node attributes are identifier and name.
Java:
System.out.printf("Node id/name: %s/%s\n",net.getNodeId(nodeHandle),
net.getNodeName(nodeHandle));Python:print("Node id/name: " + net.get_node_id(node_handle) + "/" +net.get_node_name(node_handle))
C#:
Console.WriteLine("Node id/name: {0}/{1}",net.GetNodeId(nodeHandle),
net.GetNodeName(nodeHandle));SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

100

Tutorials
The identfiers of node's outcomes are next.
Java:
System.out.print(" Outcomes:");for (String outcomeId: net.getOutcomeIds(nodeHandle)) {System.out.print(" " + outcomeId);}
Python:print("

Outcomes: " + " ".join(net.get_outcome_ids(node_handle)))C#:
foreach (String outcomeId in net.GetOutcomeIds(nodeHandle)){
Console.Write(" " + outcomeId);}
The parents of the node follow.
Java:
String[] parentIds = net.getParentIds(nodeHandle);
if (parentIds.length > 0) {System.out.print(" Parents:");for (String parentId: parentIds) {System.out.print(" " + parentId);}
System.out.println();}
Python:parent_ids = net.get_parent_ids(node_handle)
if len(parent_ids) > 0:
print(" Parents: " + " ".join(parent_ids))
C#:
String[] parentIds = net.GetParentIds(nodeHandle);
if (parentIds.Length > 0){
Console.Write(" Parents:");foreach (String parentId in parentIds){
Console.Write(" " + parentId);}
Console.WriteLine();}We proceed to display information about node's children. The code fragment is virtually identical to the iteration
over parents. Note that while tutorial uses Network.getParentIds and getChildIds to obtain identifiers ofrelated nodes, we could alternatively use the Network.getParents and getChildren, which return the node
handles (integer numbers) instead of identifiers (strings).
Finally, the node probabilities are displayed by the printCptMatrix helper. We retrieve the single-dimensional
array containing the probabilities with Network.getNodeDefinition and iterate over its elements, translatingeach index into a multi-dimensional coordinates of the elements within node's CPT. This conversion is
performed in the helper method indexToCoords, which requires the information about the outcome counts of

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

101

Tutorials
the node and its parents in its dimSizes input parameter. The first part of printCptMatrix obtains the CPT andinitializes dimSizesJava:
double[] cpt = net.getNodeDefinition(nodeHandle);
int[] parents = net.getParents(nodeHandle);
int dimCount = 1 + parents.length;
int[] dimSizes = new int[dimCount];for (int i = 0; i < dimCount - 1; i ++) {dimSizes[i] = net.getOutcomeCount(parents[i]);}dimSizes[dimSizes.length - 1] = net.getOutcomeCount(nodeHandle);
Python:cpt = net.get_node_definition(node_handle)
parents = net.get_parents(node_handle)
dim_count = 1 + len(parents)
dim_sizes = [0] * dim_countfor i in range(0, dim_count - 1):
dim_sizes[i] = net.get_outcome_count(parents[i])dim_sizes[len(dim_sizes) - 1] = net.get_outcome_count(node_handle)C#:double[] cpt = net.GetNodeDefinition(nodeHandle);
int[] parents = net.GetParents(nodeHandle);
int dimCount = 1 + parents.Length;
int[] dimSizes = new int[dimCount];for (int i = 0; i < dimCount - 1; i++){
dimSizes[i] = net.GetOutcomeCount(parents[i]);}dimSizes[dimSizes.Length - 1] = net.GetOutcomeCount(nodeHandle);
The main loop in the printCptMatrix uses performs the iteration over the elements of the CPT. The coords
integer array is filled by indexToCoords and used to print the textual information about the outcome of the nodeand parents for each element. Note that the node outcome is the rightmost entry in the coords. The parents’outcome indexes start from the left at index 0 in the coords. Part of the Tutorial3 output for the node
Forecast is show below. All lines starting with “P”( were printed by printCptMatrix.
Node: Expert forecast
Outcomes: Good Moderate Poor
Parents: Success Economy
Definition type: CPT
P(Good | Success=Success,Economy=Up)=0.7
P(Moderate | Success=Success,Economy=Up)=0.29
P(Poor | Success=Success,Economy=Up)=0.01
P(Good | Success=Success,Economy=Flat)=0.65
P(Moderate | Success=Success,Economy=Flat)=0.3
P(Poor | Success=Success,Economy=Flat)=0.05
P(Good | Success=Success,Economy=Down)=0.6
P(Moderate | Success=Success,Economy=Down)=0.3
P(Poor | Success=Success,Economy=Down)=0.1
P(Good | Success=Failure,Economy=Up)=0.15
P(Moderate | Success=Failure,Economy=Up)=0.3
P(Poor | Success=Failure,Economy=Up)=0.55

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

Tutorials

102

P(Good | Success=Failure,Economy=Flat)=0.1
P(Moderate | Success=Failure,Economy=Flat)=0.3
P(Poor | Success=Failure,Economy=Flat)=0.6
P(Good | Success=Failure,Economy=Down)=0.05
P(Moderate | Success=Failure,Economy=Down)=0.25
P(Poor | Success=Failure,Economy=Down)=0.7

7.3.1

Tutorial3.java

package tutorials;
import smile.*;// Tutorial3 loads the XDSL file and prints the information// about the structure (nodes and arcs) and the parameters// (conditional probabilities of the nodes) of the network.
public class Tutorial3 {public static void run() {System.out.println("Starting Tutorial3...");
Network net = new Network();// load the network created by Tutorial1
net.readFile("tutorial1.xdsl");for (int h = net.getFirstNode(); h >= 0; h = net.getNextNode(h)) {printNodeInfo(net, h);}System.out.println("Tutorial3 complete.");}private static void printNodeInfo(Network net, int nodeHandle) {System.out.printf("Node id/name: %s/%s\n",net.getNodeId(nodeHandle),
net.getNodeName(nodeHandle));System.out.print(" Outcomes:");for (String outcomeId: net.getOutcomeIds(nodeHandle)) {System.out.print(" " + outcomeId);}
System.out.println();
String[] parentIds = net.getParentIds(nodeHandle);
if (parentIds.length > 0) {System.out.print(" Parents:");for (String parentId: parentIds) {System.out.print(" " + parentId);}
System.out.println();}
String[] childIds = net.getChildIds(nodeHandle);
if (childIds.length > 0) {SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

103

Tutorials
System.out.print(" Children:");for (String childId: childIds) {System.out.print(" " + childId);}
System.out.println();}printCptMatrix(net, nodeHandle);}
private static void printCptMatrix(Network net, int nodeHandle) {double[] cpt = net.getNodeDefinition(nodeHandle);
int[] parents = net.getParents(nodeHandle);
int dimCount = 1 + parents.length;
int[] dimSizes = new int[dimCount];for (int i = 0; i < dimCount - 1; i ++) {dimSizes[i] = net.getOutcomeCount(parents[i]);}dimSizes[dimSizes.length - 1] = net.getOutcomeCount(nodeHandle);
int[] coords = new int[dimCount];for (int elemIdx = 0; elemIdx < cpt.length; elemIdx ++) {indexToCoords(elemIdx, dimSizes, coords);
String outcome = net.getOutcomeId(nodeHandle, coords[dimCount - 1]);
System.out.printf("
P(%s", outcome);
if (dimCount > 1) {System.out.print(" | ");for (int parentIdx = 0; parentIdx < parents.length; parentIdx++){if (parentIdx > 0) System.out.print(",");
int parentHandle = parents[parentIdx];
System.out.printf("%s=%s",net.getNodeId(parentHandle),
net.getOutcomeId(parentHandle, coords[parentIdx]));}}
double prob = cpt[elemIdx];
System.out.printf(")=%f\n", prob);}}
private static void indexToCoords(int index, int[] dimSizes, int[] coords) {int prod = 1;
for (int i = dimSizes.length - 1; i >= 0; i --) {coords[i] = (index / prod) % dimSizes[i];prod *= dimSizes[i];}}}SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

104

Tutorials
7.3.2

Tutorial3.py

import pysmile# Tutorial3 loads the XDSL file and prints the information# about the structure (nodes and arcs) and the parameters# (conditional probabilities of the nodes) of the network.
class Tutorial3:
def __init__(self):
print("Starting Tutorial3...")
net = pysmile.Network()# load the network created by Tutorial1
net.read_file("tutorial1.xdsl")
for h in net.get_all_nodes():self.print_node_info(net, h)print("Tutorial3 complete.")def print_node_info(self, net, node_handle):
print(f"Node id/name: {net.get_node_id(node_handle)}/{net.get_node_name(node_handle)}")print(f" Outcomes: {' '.join(net.get_outcome_ids(node_handle))}")
parent_ids = net.get_parent_ids(node_handle)
if len(parent_ids) > 0:
print(f" Parents: {' '.join(parent_ids)}")child_ids = net.get_child_ids(node_handle)
if len(child_ids) > 0:
print(f" Children: {' '.join(child_ids)}")self.print_cpt_matrix(net, node_handle)
def print_cpt_matrix(self, net, node_handle):
cpt = net.get_node_definition(node_handle)
parents = net.get_parents(node_handle)
dim_count = 1 + len(parents)
dim_sizes = [0] * dim_countfor i in range(0, dim_count - 1):
dim_sizes[i] = net.get_outcome_count(parents[i])dim_sizes[len(dim_sizes) - 1] = net.get_outcome_count(node_handle)
coords = [0] * dim_countfor elem_idx in range(0, len(cpt)):
self.index_to_coords(elem_idx, dim_sizes, coords)
outcome = net.get_outcome_id(node_handle, coords[dim_count - 1])
print(f"P({outcome}", end="")if dim_count > 1:
print(" | ", end="")for parent_idx in range(0, len(parents)):
if parent_idx > 0:
print(",", end="")parent_handle = parents[parent_idx]

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

105

Tutorials

print(f"{net.get_node_id(parent_handle)}="+ f"{net.get_outcome_id(parent_handle, coords[parent_idx])}", end="")prob = cpt[elem_idx]
print(f")={prob}")
def index_to_coords(self, index, dim_sizes, coords):
prod = 1
for i in range(len(dim_sizes) - 1, -1, -1):
coords[i] = int(index / prod) % dim_sizes[i]prod *= dim_sizes[i]7.3.3

Tutorial3.R

library(rSMILE)
source("License.R")# Tutorial3 loads the XDSL file and prints the information# about the structure (nodes and arcs) and the parameters# (conditional probabilities of the nodes) of the network.
indexToCoords = function(index, dimSizes) {prod <- 1L
coords <- integer(length=length(dimSizes))for (i in length(dimSizes):1) {coords[i] <- floor(index / prod) %% dimSizes[[i]]prod <- prod * dimSizes[[i]]}return(coords)}
printCptMatrix = function(net, nodeHandle) {cpt <- net$getNodeDefinition(nodeHandle)
parents <- net$getParents(nodeHandle)
dimCount <- 1 + length(parents)
dimSizes <- sapply(parents, function(x) net$getOutcomeCount(x))
dimSizes[length(dimSizes)+1] <- net$getOutcomeCount(nodeHandle)for (elemIdx in 0:(length(cpt)-1)) {coords <- indexToCoords(elemIdx, dimSizes)
outcome <- net$getOutcomeId(nodeHandle, coords[dimCount])cat("
P(", outcome, sep="");
if (dimCount > 1) {cat(" | ")parentIds <- sapply(parents, function(x) net$getNodeId(x))
outcomeIds <- sapply(1:length(parents),function(x) net$getOutcomeId(parents[x], coords[x]))cat(paste(parentIds, outcomeIds, sep="=", collapse=","))}
prob <- cpt[elemIdx + 1]
cat(sprintf(")=%f\n", prob))}}

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

Tutorials

106

printNodeInfo = function(net, nodeHandle) {cat(sprintf("Node id/name: %s/%s\n",
net$getNodeId(nodeHandle),
net$getNodeName(nodeHandle)))cat(" Outcomes: ")cat(paste(net$getOutcomeIds(nodeHandle), collapse=" "))cat("\n")parentIds <- net$getParentIds(nodeHandle)
if (length(parentIds) > 0) {cat(" Parents: ")cat(paste(parentIds, collapse=" "))cat("\n")}childIds <- net$getChildIds(nodeHandle)
if (length(childIds) > 0) {cat(" Children: ")cat(paste(childIds, collapse=" "))cat("\n")}printCptMatrix(net, nodeHandle)}
cat("Starting Tutorial3...\n")
net <- Network()# load the network created by Tutorial1
net$readFile("tutorial1.xdsl")
nodes <- net$getAllNodes()for (h in nodes) {printNodeInfo(net, h)}cat("Tutorial3 complete.\n")7.3.4

Tutorial3.cs

using System;
using Smile;// Tutorial3 loads the XDSL file and prints the information// about the structure (nodes and arcs) and the parameters// (conditional probabilities of the nodes) of the network.
namespace SmileNetTutorial{class Tutorial3{
public static void Run(){

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

107

Tutorials
Console.WriteLine("Starting Tutorial3...");
Network net = new Network();// load the network created by Tutorial1
net.ReadFile("tutorial1.xdsl");for (int h = net.GetFirstNode(); h >= 0; h = net.GetNextNode(h)){
PrintNodeInfo(net, h);}Console.WriteLine("Tutorial3 complete.");}private static void PrintNodeInfo(Network net, int nodeHandle){
Console.WriteLine("Node id/name: {0}/{1}",net.GetNodeId(nodeHandle),
net.GetNodeName(nodeHandle));Console.Write(" Outcomes:");foreach (String outcomeId in net.GetOutcomeIds(nodeHandle)){
Console.Write(" " + outcomeId);}
Console.WriteLine();String[] parentIds = net.GetParentIds(nodeHandle);
if (parentIds.Length > 0){
Console.Write(" Parents:");foreach (String parentId in parentIds){
Console.Write(" " + parentId);}
Console.WriteLine();}String[] childIds = net.GetChildIds(nodeHandle);
if (childIds.Length > 0){
Console.Write(" Children:");foreach (String childId in childIds){
Console.Write(" " + childId);}
Console.WriteLine();}PrintCptMatrix(net, nodeHandle);}
private static void PrintCptMatrix(Network net, int nodeHandle){
double[] cpt = net.GetNodeDefinition(nodeHandle);

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

108

Tutorials
int[] parents = net.GetParents(nodeHandle);
int dimCount = 1 + parents.Length;
int[] dimSizes = new int[dimCount];for (int i = 0; i < dimCount - 1; i++){
dimSizes[i] = net.GetOutcomeCount(parents[i]);}dimSizes[dimSizes.Length - 1] = net.GetOutcomeCount(nodeHandle);
int[] coords = new int[dimCount];for (int elemIdx = 0; elemIdx < cpt.Length; elemIdx++){
IndexToCoords(elemIdx, dimSizes, coords);
String outcome = net.GetOutcomeId(nodeHandle, coords[dimCount - 1]);
Console.Write("P({0}", outcome);
if (dimCount > 1){
Console.Write(" | ");for (int pIdx = 0; pIdx < parents.Length; pIdx++){if (pIdx > 0) Console.Write(",");
int parentHandle = parents[pIdx];
Console.Write("{0}={1}",net.GetNodeId(parentHandle),
net.GetOutcomeId(parentHandle, coords[pIdx]));}}
double prob = cpt[elemIdx];
Console.WriteLine(")={0}", prob);}}
private static void IndexToCoords(
int index, int[] dimSizes, int[] coords){
int prod = 1;
for (int i = dimSizes.Length - 1; i >= 0; i--){coords[i] = (index / prod) % dimSizes[i];prod *= dimSizes[i];
}
}
}
}

7.4

Tutorial 4: Creating the Influence Diagram

We will further expand the model created in Tutorial 1 82 and turn it into an influence diagram. To this effect,
we will add a decision node Investment decision and a utility node Financial gain. The decision will have two
possible states: Invest and DoNotInvest, which will be the two decision options under consideration. Which

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

109

Tutorials
option is chosen will impact the financial gain and this will be reflected by a directed arc from Investment
decision to Financial gain. Whether the venture succeeds of fails will also impact the financial gain and this willbe also reflected by a directed arc from Success of the venture to Financial gain.

We will show how to create this model. In the subsequent tutorial, we will show how to enter observations(evidence), how to perform inference, and how to retrieve the utilities calculated for the Financial gain node.
The programs starts by reading the file, just like Tutorial 2 93 and Tutorial 3 99 . We convert the identfier of thenode Success to node handle, which we will use later to create an arc between Success and Gain. Two new nodes
will be created by calling a createNode helper function, which is slightly modified version of thecreateCptNode from Tutorial 1 82 . The difference is that we now want to create different types of nodes.
Therefore, createNode has one additional input parameter, an integer which specifies the node type. Another
difference is that createNode needs to be able to add utility nodes, which do not have outcomes. The function
checks for the value of its outcomes input parameter and if it is null, the outcome initialization is skipped.
createNode is called to add two new nodes:· a decision node Investment , with a Network.NodeType.DECISION type identifier· an utility node Gain, with a Network.NodeType.UTILITY type identifier

With new nodes created we connect them to the rest of the model with Network.addArc. The only thing left is
the initialization of the parameters for the Gain node (Invest is a decision node and decision nodes have nonumeric parameters). Gain has two parents with two outcomes each and size of its definition is 2 x 2 x 1 = 4. The
program therefore specifies four numbers for the utilities.
Java:
double[] gainDefinition = new double[] {10000, // Utility(Invest=I, Success=S)-5000, // Utility(Invest=I, Success=F)
500,// Utility(Invest=D, Success=S)
500// Utility(Invest=D, Success=F)};
net.setNodeDefinition(g, gainDefinition);
Python:gain_definition = [SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

110

Tutorials
10000, # Utility(Invest=I, Success=S)-5000, # Utility(Invest=I, Success=F)
500,# Utility(Invest=D, Success=S)500# Utility(Invest=D, Success=F)]
net.set_node_definition(g, gain_definition)C#:double[] gainDefinition = new double[]{
10000, // Utility(Invest=I, Success=S)-5000, // Utility(Invest=I, Success=F)
500,// Utility(Invest=D, Success=S)
500// Utility(Invest=D, Success=F)};
net.SetNodeDefinition(g, gainDefinition);
The influence diagram is now complete, so we can write its contents to the file and exit the function. Tutorial
5 114 will load the file and perform the inference.

7.4.1

Tutorial4.java

package tutorials;
import smile.*;// Tutorial4 loads the XDSL file file created by Tutorial1// and adds decision and utility nodes, which transforms// a Bayesian Network (BN) into an Influence Diagram (ID).
public class Tutorial4 {public static void run() {System.out.println("Starting Tutorial4...");
Network net = new Network();net.readFile("tutorial1.xdsl");
int s = net.getNode("Success");
int i = createNode(net, Network.NodeType.DECISION,"Invest", "Investment decision",
new String[]{ "Invest", "DoNotInvest" }, 160, 240);
int g = createNode(net, Network.NodeType.UTILITY,"Gain", "Financial gain", null, 60, 200);
net.addArc(i, g);net.addArc(s, g);double[] gainDefinition = new double[] {10000, // Utility(Invest=I, Success=S)-5000, // Utility(Invest=I, Success=F)
500,// Utility(Invest=D, Success=S)
500// Utility(Invest=D, Success=F)};
net.setNodeDefinition(g, gainDefinition);

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

Tutorials

111

net.writeFile("tutorial4.xdsl");
System.out.println("Tutorial4 complete: Influence diagram written to tutorial4.xdsl.");}private static int createNode(
Network net, int nodeType, String id, String name,
String[] outcomes, int xPos, int yPos) {int handle = net.addNode(nodeType, id);
net.setNodeName(handle, name);
net.setNodePosition(handle, xPos, yPos, 85, 55);
if (outcomes != null) {int initialOutcomeCount = net.getOutcomeCount(handle);for (int i = 0; i < initialOutcomeCount; i ++) {net.setOutcomeId(handle, i, outcomes[i]);}for (int i = initialOutcomeCount; i < outcomes.length; i ++) {net.addOutcome(handle, outcomes[i]);}}return handle;
}
}

7.4.2

Tutorial4.py

import pysmile# Tutorial4 loads the XDSL file file created by Tutorial1# and adds decision and utility nodes, which transforms# a Bayesian Network (BN) into an Influence Diagram (ID).
class Tutorial4:
def __init__(self):
print("Starting Tutorial4...")
net = pysmile.Network()
net.read_file("tutorial1.xdsl")
s = net.get_node("Success")i = self.create_node(net, pysmile.NodeType.DECISION,"Invest", "Investment decision",["Invest", "DoNotInvest"], 160, 240)g = self.create_node(net, pysmile.NodeType.UTILITY,"Gain", "Financial gain", None, 60, 200)
net.add_arc(i, g)
net.add_arc(s, g)

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

112

Tutorials
gain_definition = [10000, # Utility(Invest=I, Success=S)-5000, # Utility(Invest=I, Success=F)
500,# Utility(Invest=D, Success=S)500# Utility(Invest=D, Success=F)]
net.set_node_definition(g, gain_definition)
net.write_file("tutorial4.xdsl")
print("Tutorial4 complete: Influence diagram written to tutorial4.xdsl.")def create_node(self,
net, node_type, id, name,outcomes, xPos, yPos):
handle = net.add_node(node_type, id)
net.set_node_name(handle, name)net.set_node_position(handle, xPos, yPos, 85, 55)
if outcomes is not None:
initial_outcome_count = net.get_outcome_count(handle)
for i in range(0, initial_outcome_count):
net.set_outcome_id(handle, i, outcomes[i])for i in range(initial_outcome_count, len(outcomes)):
net.add_outcome(handle, outcomes[i])return handle

7.4.3

Tutorial4.R

library(rSMILE)
source("License.R")# Tutorial4 loads the XDSL file file created by Tutorial1# and adds decision and utility nodes, which transforms# a Bayesian Network (BN) into an Influence Diagram (ID).
createNode = function(net, nodeType, id, name, outcomes, xPos, yPos) {handle <- net$addNode(nodeType, id)
initialOutcomeCount <- net$getOutcomeCount(handle)
count <- length(outcomes)
net$setNodeName(handle, name)net$setNodePosition(handle, xPos, yPos, 85L, 55L)
if (!is.null(outcomes)) {sapply(0:(initialOutcomeCount-1),function(x) net$setOutcomeId(handle, x, outcomes[x+1]))
if (initialOutcomeCount < count) {sapply(initialOutcomeCount:(count-1),function(x) net$addOutcome(handle, outcomes[x+1]))}}
return(handle)

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

113

Tutorials}
cat("Starting Tutorial4...\n")
net <- Network()
net$readFile("tutorial1.xdsl")
s <- net$getNode("Success")i <- createNode(net, net$NodeType$DECISION, "Invest", "Investment decision",c("Invest", "DoNotInvest"), 160L, 240L)
g <- createNode(net, net$NodeType$UTILITY, "Gain", "Financial Gain", NULL, 60, 200)
net$addArc(i, g)
net$addArc(s, g)
gainDefinition <- c(10000, # Utility(Invest=I, Success=S)-5000, # Utility(Invest=I, Success=F)
500,# Utility(Invest=D, Success=S)500)# Utility(Invest=D, Success=F)

net$setNodeDefinition(g, gainDefinition)
net$writeFile("tutorial4.xdsl")
cat("Tutorial4 complete: Influence diagram written to tutorial4.xdsl.\n")7.4.4

Tutorial4.cs

using System;
using Smile;// Tutorial4 loads the XDSL file file created by Tutorial1// and adds decision and utility nodes, which transforms// a Bayesian Network (BN) into an Influence Diagram (ID).
namespace SmileNetTutorial{class Tutorial4{
public static void Run(){
Console.WriteLine("Starting Tutorial4...");
Network net = new Network();
net.ReadFile("tutorial1.xdsl");
int s = net.GetNode("Success");
int i = CreateNode(net, Network.NodeType.Decision,"Invest", "Investment decision",
new String[] { "Invest", "DoNotInvest" }, 160, 240);

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

114

Tutorials
int g = CreateNode(net, Network.NodeType.Utility,"Gain", "Financial gain", null, 60, 200);
net.AddArc(i, g);
net.AddArc(s, g);double[] gainDefinition = new double[]{
10000, // Utility(Invest=I, Success=S)-5000, // Utility(Invest=I, Success=F)
500,// Utility(Invest=D, Success=S)
500// Utility(Invest=D, Success=F)};
net.SetNodeDefinition(g, gainDefinition);
net.WriteFile("tutorial4.xdsl");Console.WriteLine("Tutorial4 complete: ID written to tutorial4.xdsl.");}private static int CreateNode(
Network net, Network.NodeType nodeType, String id, String name,
String[] outcomes, int xPos, int yPos){
int handle = net.AddNode(nodeType, id);
net.SetNodeName(handle, name);
net.SetNodePosition(handle, xPos, yPos, 85, 55);
if (outcomes != null){
int initialOutcomeCount = net.GetOutcomeCount(handle);for (int i = 0; i < initialOutcomeCount; i++){net.SetOutcomeId(handle, i, outcomes[i]);}for (int i = initialOutcomeCount; i < outcomes.Length; i++){net.AddOutcome(handle, outcomes[i]);}}return handle;
}
}
}

7.5

Tutorial 5: Inference in an Influence Diagram

This tutorial loads the influence diagram that we have created in Tutorial 4 108 . We will perform multiple
inference calls and display calculated utilities.

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

115

Tutorials
With model loaded, we calculate the probabilities and utilities by calling Network.updateBeliefs. A helper
function, printFinancialGain, is called to print out the utilities stored in the Gain node.
printFinancialGain uses Network.getNodeValue to obtain the utilities. To properly interpret the utilities,
we also need to call Network.getValueIndexingParents, which is an array containing the handles of decisionnodes not set to evidence.
Java:double[] expectedUtility = net.getNodeValue("Gain");
int[] utilParents = net.getValueIndexingParents("Gain");printGainMatrix(net, expectedUtility, utilParents);
Python:expected_utility = net.get_node_value("Gain")
util_parents = net.get_value_indexing_parents("Gain")self.print_gain_matrix(net, expected_utility, util_parents)
C#:
double[] expectedUtility = net.GetNodeValue("Gain");
int[] utilParents = net.GetValueIndexingParents("Gain");printGainMatrix(net, expectedUtility, utilParents);
printGainMatrix is a modification of printCptMatrix from Tutorial 3 99 . It iterates over the elements of anarray and converts the linear index into multi-dimensional coordinates.
When we call printGainMatrix for the first time, the network has no evidence set. The utilities calculated
without evidence suggest that we should not invest - here's what is printed on the screen:
Financial gain:
Utility(Invest=Invest)=-1850
Utility(Invest=DoNotInvest)=500
Next, we model the analyst’s forecast to be good by calling local helper function changeEvidenceAndUpdate,
which is based on the function with the same name from Tutorial 2 93 .
Java:changeEvidenceAndUpdate(net, "Forecast", "Good");
Python:self.change_evidence_and_update(net, "Forecast", "Good")C#:
ChangeEvidenceAndUpdate(net, "Forecast", "Good");
The expected gain changes, now the optimal decision is to invest:
Financial gain:
Utility(Invest=Invest)=4455.78
Utility(Invest=DoNotInvest)=500
Now we observe the state of the economy and conclude that it is growing - the program performs anotherchangeEvidenceAndUpdate call . Growing economy makes our chances even better:
Financial gain:
Utility(Invest=Invest)=5000
Utility(Invest=DoNotInvest)=500
This concludes Tutorial 5.

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

Tutorials
7.5.1

116

Tutorial5.java

package tutorials;
import smile.*;// Tutorial5 loads the XDSL file created by Tutorial4,// then performs the series of inference calls,// changing evidence each time.
public class Tutorial5 {public static void run() {System.out.println("Starting Tutorial5...");
Network net = new Network();net.readFile("tutorial4.xdsl");
System.out.println("No evidence set.");net.updateBeliefs();
printFinancialGain(net);
System.out.println("Setting Forecast=Good.");changeEvidenceAndUpdate(net, "Forecast", "Good");
System.out.println("Adding Economy=Up");changeEvidenceAndUpdate(net, "Economy", "Up");
System.out.println("Tutorial5 complete.");}static void changeEvidenceAndUpdate(
Network net, String nodeId, String outcomeId) {if (outcomeId != null) {net.setEvidence(nodeId, outcomeId);} else {net.clearEvidence(nodeId);}
net.updateBeliefs();
printFinancialGain(net);}static void printFinancialGain(Network net) {double[] expectedUtility = net.getNodeValue("Gain");
int[] utilParents = net.getValueIndexingParents("Gain");printGainMatrix(net, expectedUtility, utilParents);}
static void printGainMatrix(Network net, double[] mtx, int[] parents) {int dimCount = 1 + parents.length;
int[] dimSizes = new int[dimCount];for (int i = 0; i < dimCount - 1; i ++) {dimSizes[i] = net.getOutcomeCount(parents[i]);}SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

117

Tutorials
dimSizes[dimSizes.length - 1] = 1;
int[] coords = new int[dimCount];for (int elemIdx = 0; elemIdx < mtx.length; elemIdx ++) {indexToCoords(elemIdx, dimSizes, coords);
System.out.print("

Utility(");

if (dimCount > 1){for (int parentIdx = 0; parentIdx < parents.length; parentIdx++){if (parentIdx > 0) System.out.print(",");
int parentHandle = parents[parentIdx];
System.out.printf("%s=%s",net.getNodeId(parentHandle),
net.getOutcomeId(parentHandle, coords[parentIdx]));}}
System.out.printf(")=%f\n", mtx[elemIdx]);}
System.out.println();}
static void indexToCoords(int index, int[] dimSizes, int[] coords) {int prod = 1;
for (int i = dimSizes.length - 1; i >= 0; i --) {coords[i] = (index / prod) % dimSizes[i];prod *= dimSizes[i];
}
}
}

7.5.2

Tutorial5.py

import pysmile# Tutorial5 loads the XDSL file created by Tutorial4,# then performs the series of inference calls,# changing evidence each time.
class Tutorial5:
def __init__(self):
print("Starting Tutorial5...")
net = pysmile.Network()
net.read_file("tutorial4.xdsl")
print("No evidence set.")net.update_beliefs()
self.print_financial_gain(net)
print("Setting Forecast=Good.")
self.change_evidence_and_update(net, "Forecast", "Good")

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

Tutorials

118

print("Adding Economy=Up")
self.change_evidence_and_update(net, "Economy", "Up")print("Tutorial5 complete.")def change_evidence_and_update(self, net, node_id, outcome_id):
if outcome_id is not None:
net.set_evidence(node_id, outcome_id)
else:
net.clear_evidence(node_id)
net.update_beliefs()
self.print_financial_gain(net)def print_financial_gain(self, net):
expected_utility = net.get_node_value("Gain")
util_parents = net.get_value_indexing_parents("Gain")self.print_gain_matrix(net, expected_utility, util_parents)
def print_gain_matrix(self, net, mtx, parents):
dim_count = 1 + len(parents)
dim_sizes = [0] * dim_countfor i in range(0, dim_count - 1):
dim_sizes[i] = net.get_outcome_count(parents[i])dim_sizes[len(dim_sizes) - 1] = 1
coords = [0] * dim_countfor elem_idx in range(0, len(mtx)):
self.index_to_coords(elem_idx, dim_sizes, coords)
print("
Utility(", end="")if dim_count > 1:
for parent_idx in range(0, len(parents)):
if parent_idx > 0:
print(",", end="")parent_handle = parents[parent_idx]
print(f"{net.get_node_id(parent_handle)}="+ f"{net.get_outcome_id(parent_handle, coords[parent_idx])}", end="")print(f")={mtx[elem_idx]}")print()
def index_to_coords(self, index, dim_sizes, coords):
prod = 1
for i in range(len(dim_sizes) - 1, -1, -1):
coords[i] = int(index / prod) % dim_sizes[i]prod *= dim_sizes[i]7.5.3

Tutorial5.R

library(rSMILE)
source("License.R")# Tutorial5 loads the XDSL file created by Tutorial4,# then performs the series of inference calls,# changing evidence each time.

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

Tutorials

119

indexToCoords = function(index, dimSizes) {prod <- 1L
coords <- integer(length=length(dimSizes))for (i in length(dimSizes):1) {coords[i] <- floor(index / prod) %% dimSizes[[i]]prod <- prod * dimSizes[[i]]}return(coords)}
printGainMatrix = function(net, mtx, parents) {dimCount <- 1 + length(parents)
dimSizes <- sapply(parents, function(x) net$getOutcomeCount(x))
dimSizes[length(dimSizes)+1] <- 1
for (elemIdx in 0:(length(mtx)-1)) {coords <- indexToCoords(elemIdx, dimSizes)
cat("
Utility(");
if (dimCount > 1) {parentIds <- sapply(parents, function(x) net$getNodeId(x))
outcomeIds <- sapply(1:length(parents),function(x) net$getOutcomeId(parents[x], coords[x]))cat(paste(parentIds, outcomeIds, sep="=", collapse=","))}
cat(sprintf(")=%f\n", mtx[elemIdx+1]))}}
printFinancialGain = function(net) {expectedUtility <- net$getNodeValue("Gain")utilParents <- net$getValueIndexingParents("Gain")
printGainMatrix(net, expectedUtility, utilParents)}changeEvidenceAndUpdate = function(net, nodeId, outcomeId) {if (!is.null(outcomeId)) {net$setEvidence(nodeId, outcomeId)} else {net$clearEvidence(nodeId)}
net$updateBeliefs()
printFinancialGain(net)}cat("Starting Tutorial5...\n")
net <- Network()
net$readFile("tutorial4.xdsl")
cat("No evidence set.\n")
net$updateBeliefs()
printFinancialGain(net)

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

120

Tutorials
cat("Setting Forecast=Good.\n")changeEvidenceAndUpdate(net, "Forecast", "Good")cat("Adding Economy=Up.\n")changeEvidenceAndUpdate(net, "Economy", "Up")cat("Tutorial5 complete.\n")7.5.4

Tutorial5.cs

using System;
using Smile;// Tutorial5 loads the XDSL file created by Tutorial4,// then performs the series of inference calls,// changing evidence each time.
namespace SmileNetTutorial{class Tutorial5{
public static void Run(){
Console.WriteLine("Starting Tutorial5...");
Network net = new Network();
net.ReadFile("tutorial4.xdsl");Console.WriteLine("No evidence set.");net.UpdateBeliefs();
PrintFinancialGain(net);
Console.WriteLine("Setting Forecast=Good.");ChangeEvidenceAndUpdate(net, "Forecast", "Good");
Console.WriteLine("Adding Economy=Up");
ChangeEvidenceAndUpdate(net, "Economy", "Up");
Console.WriteLine("Tutorial5 complete.");}static void ChangeEvidenceAndUpdate(Network net, String nodeId, String outcomeId){
if (outcomeId != null){
net.SetEvidence(nodeId, outcomeId);}
else{
net.ClearEvidence(nodeId);}
net.UpdateBeliefs();
PrintFinancialGain(net);}

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

121

Tutorials
static void PrintFinancialGain(Network net){
double[] expectedUtility = net.GetNodeValue("Gain");
int[] utilParents = net.GetValueIndexingParents("Gain");
PrintGainMatrix(net, expectedUtility, utilParents);}
static void PrintGainMatrix(Network net, double[] mtx, int[] parents){
int dimCount = 1 + parents.Length;
int[] dimSizes = new int[dimCount];for (int i = 0; i < dimCount - 1; i++){
dimSizes[i] = net.GetOutcomeCount(parents[i]);}dimSizes[dimSizes.Length - 1] = 1;
int[] coords = new int[dimCount];for (int elemIdx = 0; elemIdx < mtx.Length; elemIdx++){
IndexToCoords(elemIdx, dimSizes, coords);
Console.Write("
Utility(");
if (dimCount > 1){for (int pIdx = 0; pIdx < parents.Length; pIdx++){if (pIdx > 0) Console.Write(",");
int parentHandle = parents[pIdx];
Console.Write("{0}={1}",net.GetNodeId(parentHandle),
net.GetOutcomeId(parentHandle, coords[pIdx]));}}Console.Write(")={0}\n", mtx[elemIdx]);}
Console.WriteLine();}static void IndexToCoords(int index, int[] dimSizes, int[] coords){
int prod = 1;
for (int i = dimSizes.Length - 1; i >= 0; i--){coords[i] = (index / prod) % dimSizes[i];prod *= dimSizes[i];}}}}SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

122

Tutorials
7.6

Tutorial 6: Dynamic model

Consider the following example, inspired by (Russell & Norvig, 1995), in which a security guard at some secretunderground installation works on a shift of seven days and wants to know whether it is raining on the day ofher return to the outside world. Her only access to the outside world occurs each morning when she sees the
director coming in, with or without an umbrella. Furthermore, she knows that the government has two secretunderground installations: one in Pittsburgh and one in the Sahara, but she does not know which one she isguarding. For each day t, the set of evidence contains a single variable Umbrellat (observation of an umbrella
carried by the director) and the set of unobservable variables contains Raint (a propositional variable with twostates true and false, denoting whether it is raining) and Location (with two possible states: Pittsburgh and
Sahara). The prior probability of rain depends on the geographical location and on whether it rained on theprevious day. For simplicity, we do not use the initial and terminal condition nodes in this tutorial.

The model contains three discrete chance nodes (CPT), created with a call to createCptNode helper function,
just like in nodes in Tutorial 1 82 . The Rain and Umbrella nodes are marked as belonging to the temporal plateby calling Network.SetNodeTemporalType:Java:
net.setNodeTemporalType(rain, Network.NodeTemporalType.PLATE);
net.setNodeTemporalType(umb, Network.NodeTemporalType.PLATE);
Python:net.set_node_temporal_type(rain, pysmile.NodeTemporalType.PLATE)
net.set_node_temporal_type(umb, pysmile.NodeTemporalType.PLATE)
C#:
net.SetNodeTemporalType(rain, Network.NodeTemporalType.Plate);
net.SetNodeTemporalType(umb, Network.NodeTemporalType.Plate);

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

123

Tutorials
Two of the three arcs in the model are created by Network.addArc. The temporal arc expressing the dependencyof Raint on Raint-1 is created by the Network.addTemporalArc method. Please note the temporal order of thearc passed as the 3rd parameter:Java:
net.addArc(loc, rain);net.addTemporalArc(rain, rain, 1);
net.addArc(rain, umb);
Python:net.add_arc(loc, rain)
net.add_temporal_arc(rain, rain, 1)
net.add_arc(rain, umb)C#:
net.AddArc(loc, rain);
net.AddTemporalArc(rain, rain, 1);
net.AddArc(rain, umb);
CPTs for the nodes are initialized with Network.setNodeDefinition, as in Tutorial 1 82 . However, the node
Rain requires two CPTs, because it has an incoming temporal arc of order 1. The additional CPT is set by the callto Network.setNodeTemporalDefinition:Java:double[] rainDefTemporal = new double[] {0.7,// P(Rain=true |Location=Pittsburgh,Rain[t-1]=true)
0.3,// P(Rain=false|Location=Pittsburgh,Rain[t-1]=true)
0.3,// P(Rain=true |Location=Pittsburgh,Rain[t-1]=false)
0.7,// P(Rain=false|Location=Pittsburgh,Rain[t-1]=false)
0.001, // P(Rain=true |Location=Sahara,Rain[t-1]=true)
0.999, // P(Rain=false|Location=Sahara,Rain[t-1]=true)
0.01, // P(Rain=true |Location=Sahara,Rain[t-1]=false)
0.99 // P(Rain=false|Location=Sahara,Rain[t-1]=false)};
net.setNodeTemporalDefinition(rain, 1, rainDefTemporal);Python:rain_def_temporal = [# probabilities go here]
net.set_node_temporal_definition(rain, 1, rain_def_temporal)C#:
double[] rainDefTemporal = new double[]{// probabilities go here};
net.SetNodeTemporalDefinition(rain, 1, rainDefTemporal);
Finally, we adjust the number of slices created during the network unrolling with a call to
Network.setSliceCount. The network is complete. The program proceeds to the inference, first without any
evidence in the network, then with two observations of the Umbrella, set in the time steps t=1 and t=3:
Java:
net.setTemporalEvidence(umb, 1, 0);

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

124

Tutorials
net.setTemporalEvidence(umb, 3, 1);
Python:net.set_temporal_evidence(umb, 1, 0)
net.set_temporal_evidence(umb, 3, 1
C#:
net.SetTemporalEvidence(umb, 1, 0);net.SetTemporalEvidence(umb, 3, 1);
In dynamic Bayesian networks, the plate nodes have their beliefs calculated for each time slice. The number of
elements in the node value matrix for these nodes is a product of outcome count and slice count. The helper
function updateAndShowTemporalResults iterates over this matrix using two nested loops in order to print theresults. Since the probabilities for the same slice are adjacent, the inner loop uses the product of the outcome
count and slice index from the outer loop as a base index.
Java:double[] v = net.getNodeValue(h);for (int sliceIdx = 0; sliceIdx < sliceCount; sliceIdx++) {System.out.printf("\tt=%d:", sliceIdx);
for (int i = 0; i < outcomeCount; i++) {System.out.printf(" %f", v[sliceIdx * outcomeCount + i]);}
System.out.println();}
Python:v = net.get_node_value(h)for slice_idx in range(0, slice_count):s = "\tt=" + str(slice_idx) + ":"for i in range(0, outcome_count):s = s + " " + str(v[slice_idx * outcome_count + i])print(s)C#:double[] v = net.GetNodeValue(h);for (int sliceIdx = 0; sliceIdx < sliceCount; sliceIdx++){
Console.Write("\tt={0}:", sliceIdx);
for (int i = 0; i < outcomeCount; i++){Console.Write(" {0}", v[sliceIdx * outcomeCount + i]);}Console.WriteLine();
}

7.6.1

Tutorial6.java

package tutorials;
import smile.*;// Tutorial6 creates a dynamic Bayesian network (DBN),// performs the inference, then saves the model to disk.

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

Tutorials

125

public class Tutorial6 {public static void run() {System.out.println("Starting Tutorial6...");
Network net = new Network();
int loc = createCptNode(net, "Location", "Location",
new String[] { "Pittsburgh", "Sahara" },160, 360);
int rain = createCptNode(net, "Rain", "Rain",new String[] { "true", "false" },380, 240);
int umb = createCptNode(net, "Umbrella", "Umbrella",
new String[] { "true", "false" },300, 100);
net.setNodeTemporalType(rain, Network.NodeTemporalType.PLATE);
net.setNodeTemporalType(umb, Network.NodeTemporalType.PLATE);
net.addArc(loc, rain);net.addTemporalArc(rain, rain, 1);
net.addArc(rain, umb);
double[] rainDef = new double[] {0.7, // P(Rain=true |Location=Pittsburgh)
0.3, // P(Rain=false|Location=Pittsburgh)
0.01, // P(Rain=true |Location=Sahara)
0.99 // P(Rain=false|Location=Sahara)};net.setNodeDefinition(rain, rainDef);double[] rainDefTemporal = new double[] {0.7,// P(Rain=true |Location=Pittsburgh,Rain[t-1]=true)
0.3,// P(Rain=false|Location=Pittsburgh,Rain[t-1]=true)
0.3,// P(Rain=true |Location=Pittsburgh,Rain[t-1]=false)
0.7,// P(Rain=false|Location=Pittsburgh,Rain[t-1]=false)
0.001, // P(Rain=true |Location=Sahara,Rain[t-1]=true)
0.999, // P(Rain=false|Location=Sahara,Rain[t-1]=true)
0.01, // P(Rain=true |Location=Sahara,Rain[t-1]=false)
0.99 // P(Rain=false|Location=Sahara,Rain[t-1]=false)};
net.setNodeTemporalDefinition(rain, 1, rainDefTemporal);double[] umbDef = new double[] {0.9, // P(Umbrella=true |Rain=true)
0.1, // P(Umbrella=false|Rain=true)
0.2, // P(Umbrella=true |Rain=false)
0.8 // P(Umbrella=false|Rain=false)};
net.setNodeDefinition(umb, umbDef);

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

126

Tutorials
net.setSliceCount(5);System.out.println("Performing update without evidence.");updateAndShowTemporalResults(net);
System.out.println("Setting Umbrella[t=1] to true and Umbrella[t=3] to false.");net.setTemporalEvidence(umb, 1, 0);
net.setTemporalEvidence(umb, 3, 1);
updateAndShowTemporalResults(net);
net.writeFile("tutorial6.xdsl");
System.out.println("Tutorial6 complete: Network written to tutorial6.xdsl");}private static void updateAndShowTemporalResults(Network net) {net.updateBeliefs();
int sliceCount = net.getSliceCount();
for (int h = net.getFirstNode(); h >= 0; h = net.getNextNode(h)) {if (net.getNodeTemporalType(h) == Network.NodeTemporalType.PLATE) {int outcomeCount = net.getOutcomeCount(h);System.out.printf("Temporal beliefs for %s:\n", net.getNodeId(h));
double[] v = net.getNodeValue(h);for (int sliceIdx = 0; sliceIdx < sliceCount; sliceIdx++) {System.out.printf("\tt=%d:", sliceIdx);
for (int i = 0; i < outcomeCount; i++) {System.out.printf(" %f", v[sliceIdx*outcomeCount+i]);}
System.out.println();}}}
System.out.println();}private static int createCptNode(
Network net, String id, String name,
String[] outcomes, int xPos, int yPos) {int handle = net.addNode(Network.NodeType.CPT, id);
net.setNodeName(handle, name);
net.setNodePosition(handle, xPos, yPos, 85, 55);
int initialOutcomeCount = net.getOutcomeCount(handle);for (int i = 0; i < initialOutcomeCount; i ++) {net.setOutcomeId(handle, i, outcomes[i]);}for (int i = initialOutcomeCount; i < outcomes.length; i ++) {net.addOutcome(handle, outcomes[i]);}return handle;}

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

Tutorials

127

}

7.6.2

Tutorial6.py

import pysmile# Tutorial6 creates a dynamic Bayesian network (DBN),# performs the inference, then saves the model to disk.
class Tutorial6:
def __init__(self):
print("Starting Tutorial6...")
net = pysmile.Network()
loc = self.create_cpt_node(net,"Location", "Location",["Pittsburgh", "Sahara"],160, 360)
rain = self.create_cpt_node(net,"Rain", "Rain",["true", "false"],380, 240)
umb = self.create_cpt_node(net,"Umbrella", "Umbrella",["true", "false"],300, 100)
net.set_node_temporal_type(rain, pysmile.NodeTemporalType.PLATE)
net.set_node_temporal_type(umb, pysmile.NodeTemporalType.PLATE)
net.add_arc(loc, rain)
net.add_temporal_arc(rain, rain, 1)
net.add_arc(rain, umb)
rain_def = [0.7, # P(Rain=true |Location=Pittsburgh)
0.3, # P(Rain=false|Location=Pittsburgh)
0.01, # P(Rain=true |Location=Sahara)
0.99 # P(Rain=false|Location=Sahara)]net.set_node_definition(rain, rain_def)
rain_def_temporal = [0.7,# P(Rain=true |Location=Pittsburgh,Rain[t-1]=true)
0.3,# P(Rain=false|Location=Pittsburgh,Rain[t-1]=true)
0.3,# P(Rain=true |Location=Pittsburgh,Rain[t-1]=false)
0.7,# P(Rain=false|Location=Pittsburgh,Rain[t-1]=false)
0.001, # P(Rain=true |Location=Sahara,Rain[t-1]=true)
0.999, # P(Rain=false|Location=Sahara,Rain[t-1]=true)
0.01, # P(Rain=true |Location=Sahara,Rain[t-1]=false)
0.99# P(Rain=false|Location=Sahara,Rain[t-1]=false)]
net.set_node_temporal_definition(rain, 1, rain_def_temporal)
umb_def = [SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

128

Tutorials
0.9, # P(Umbrella=true |Rain=true)
0.1, # P(Umbrella=false|Rain=true)
0.2, # P(Umbrella=true |Rain=false)
0.8 # P(Umbrella=false|Rain=false)]
net.set_node_definition(umb, umb_def)
net.set_slice_count(5)print("Performing update without evidence.")self.update_and_show_temporal_results(net)
print("Setting Umbrella[t=1], to true and Umbrella[t=3] to false.")net.set_temporal_evidence(umb, 1, 0)
net.set_temporal_evidence(umb, 3, 1)
self.update_and_show_temporal_results(net)net.write_file("tutorial6.xdsl")
print("Tutorial6 complete: Network written to tutorial6.xsdl")
def update_and_show_temporal_results(self, net):
net.update_beliefs()
slice_count = net.get_slice_count()for h in net.get_all_nodes():
if net.get_node_temporal_type(h) == pysmile.NodeTemporalType.PLATE:
outcome_count = net.get_outcome_count(h)v = net.get_node_value(h)print(f"Temporal beliefs for {net.get_node_id(h)}:")
for slice_idx in range(0, slice_count):
print(f"\tt={slice_idx}:", end="")for i in range(0, outcome_count):
print(f" {v[slice_idx * outcome_count + i]}", end="")print()
def create_cpt_node(self, net, id, name, outcomes, x_pos, y_pos):handle = net.add_node(pysmile.NodeType.CPT, id)
net.set_node_name(handle, name)net.set_node_position(handle, x_pos, y_pos, 85, 55)
initial_outcome_count = net.get_outcome_count(handle)
for i in range(0, initial_outcome_count):
net.set_outcome_id(handle, i, outcomes[i])for i in range(initial_outcome_count, len(outcomes)):
net.add_outcome(handle, outcomes[i])return handle

7.6.3

Tutorial6.R

library(rSMILE)
source("License.R")

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

129

Tutorials# Tutorial6 creates a dynamic Bayesian network (DBN),# performs the inference, then saves the model to disk.
createCptNode = function(net, id, name, outcomes, xPos, yPos) {handle <- net$addNode(net$NodeType$CPT, id)
initialOutcomeCount <- net$getOutcomeCount(handle)
count <- length(outcomes)
net$setNodeName(handle, name)net$setNodePosition(handle, xPos, yPos, 85L, 55L)
if (!is.null(outcomes)) {sapply(0:(initialOutcomeCount-1),function(x) net$setOutcomeId(handle, x, outcomes[x+1]))
if (initialOutcomeCount < count) {sapply(initialOutcomeCount:(count-1),function(x) net$addOutcome(handle, outcomes[x+1]))}}
return(handle)}
updateAndShowTemporalResults = function(net) {net$updateBeliefs()
sliceCount <- net$getSliceCount()
handles <- net$getAllNodes()for (h in handles) {if (net$getNodeTemporalType(h) == net$NodeTemporalType$PLATE) {outcomeCount <- net$getOutcomeCount(h)cat(sprintf("Temporal beliefs for %s:\n", net$getNodeId(h)))
v <- net$getNodeValue(h)for (sliceIdx in 0:(sliceCount-1)) {cat(sprintf("\tt=%d:", sliceIdx))
for (i in 0:(outcomeCount-1)) {cat(sprintf(" %f", v[(sliceIdx*outcomeCount+i)+1]))}
cat("\n")}}}cat("\n")}cat("Starting Tutorial6...\n")
net <- Network()
loc <- createCptNode(net, "Location", "Location", c("Pittsburgh", "Sahara"), 160, 360)
rain <- createCptNode(net, "Rain", "Rain",c("true", "false"), 380, 240)umb <- createCptNode(net, "Umbrella", "Umbrella", c("true", "false"), 300, 100)
net$setNodeTemporalType(rain, net$NodeTemporalType$PLATE)
net$setNodeTemporalType(umb, net$NodeTemporalType$PLATE)
net$addArc(loc, rain)
net$addTemporalArc(rain, rain, 1)
net$addArc(rain, umb)

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

Tutorials

130

rainDef <- c( 0.7,
0.3,
0.01,
0.99 )
net$setNodeDefinition(rain, rainDef)
rainDefTemporal <- c( 0.7,
0.3,
0.3,
0.7,
0.001,
0.999,
0.01,
0.99 )
net$setNodeTemporalDefinition(rain, 1, rainDefTemporal)
umbDef <- c( 0.9,
0.1,
0.2,
0.8 )
net$setNodeDefinition(umb, umbDef)
net$setSliceCount(5)cat("Performing update without evidence.\n")updateAndShowTemporalResults(net)cat("Setting Umbrella[t=1] to true and Umbrella[t=3] to false.\n")
net$setTemporalEvidence(umb, 1, 0)
net$setTemporalEvidence(umb, 3, 1)
updateAndShowTemporalResults(net)
net$writeFile("tutorial6.xdsl")
cat("Tutorial6 complete: Network written to tutorial6.xdsl\n")7.6.4

Tutorial6.cs

using System;
using Smile;// Tutorial6 creates a dynamic Bayesian network (DBN),// performs the inference, then saves the model to disk.
namespace SmileNetTutorial{class Tutorial6{
public static void Run(){
Console.WriteLine("Starting Tutorial6...");
Network net = new Network();

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

Tutorials

131

int loc = CreateCptNode(net, "Location", "Location",
new String[] { "Pittsburgh", "Sahara" },160, 360);
int rain = CreateCptNode(net, "Rain", "Rain",new String[] { "true", "false" },380, 240);
int umb = CreateCptNode(net, "Umbrella", "Umbrella",
new String[] { "true", "false" },300, 100);
net.SetNodeTemporalType(rain, Network.NodeTemporalType.Plate);
net.SetNodeTemporalType(umb, Network.NodeTemporalType.Plate);
net.AddArc(loc, rain);
net.AddTemporalArc(rain, rain, 1);
net.AddArc(rain, umb);
double[] rainDef = new double[]{
0.7, // P(Rain=true |Location=Pittsburgh)
0.3, // P(Rain=false|Location=Pittsburgh)
0.01, // P(Rain=true |Location=Sahara)
0.99 // P(Rain=false|Location=Sahara)};net.SetNodeDefinition(rain, rainDef);double[] rainDefTemporal = new double[]{
0.7,// P(Rain=true |Location=Pittsburgh,Rain[t-1]=true)
0.3,// P(Rain=false|Location=Pittsburgh,Rain[t-1]=true)
0.3,// P(Rain=true |Location=Pittsburgh,Rain[t-1]=false)
0.7,// P(Rain=false|Location=Pittsburgh,Rain[t-1]=false)
0.001, // P(Rain=true |Location=Sahara,Rain[t-1]=true)
0.999, // P(Rain=false|Location=Sahara,Rain[t-1]=true)
0.01, // P(Rain=true |Location=Sahara,Rain[t-1]=false)
0.99 // P(Rain=false|Location=Sahara,Rain[t-1]=false)};
net.SetNodeTemporalDefinition(rain, 1, rainDefTemporal);double[] umbDef = new double[]{
0.9, // P(Umbrella=true |Rain=true)
0.1, // P(Umbrella=false|Rain=true)
0.2, // P(Umbrella=true |Rain=false)
0.8 // P(Umbrella=false|Rain=false)};
net.SetNodeDefinition(umb, umbDef);
net.SetSliceCount(5);SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

132

Tutorials
Console.WriteLine("Performing update without evidence.");UpdateAndShowTemporalResults(net);

Console.WriteLine("Setting Umbrella[t=1] to true and Umbrella[t=3] to false.");net.SetTemporalEvidence(umb, 1, 0);net.SetTemporalEvidence(umb, 3, 1);UpdateAndShowTemporalResults(net);
net.WriteFile("tutorial6.xdsl");Console.WriteLine("Tutorial6 complete: Network written to tutorial6.xdsl");}private static void UpdateAndShowTemporalResults(Network net){
net.UpdateBeliefs();
int sliceCount = net.GetSliceCount();
for (int h = net.GetFirstNode(); h >= 0; h = net.GetNextNode(h)){
if (net.GetNodeTemporalType(h) == Network.NodeTemporalType.Plate){
int outcomeCount = net.GetOutcomeCount(h);Console.WriteLine("Temporal beliefs for {0}:", net.GetNodeId(h));
double[] v = net.GetNodeValue(h);for (int sliceIdx = 0; sliceIdx < sliceCount; sliceIdx++){
Console.Write("\tt={0}:", sliceIdx);
for (int i = 0; i < outcomeCount; i++){Console.Write(" {0}", v[sliceIdx * outcomeCount + i]);}Console.WriteLine();}}}Console.WriteLine();}private static int CreateCptNode(
Network net, String id, String name,
String[] outcomes, int xPos, int yPos){
int handle = net.AddNode(Network.NodeType.Cpt, id);
net.SetNodeName(handle, name);
net.SetNodePosition(handle, xPos, yPos, 85, 55);
int initialOutcomeCount = net.GetOutcomeCount(handle);for (int i = 0; i < initialOutcomeCount; i++){net.SetOutcomeId(handle, i, outcomes[i]);}for (int i = initialOutcomeCount; i < outcomes.Length; i++){

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

133

Tutorials
net.AddOutcome(handle, outcomes[i]);}return handle;
}
}
}

7.7

Tutorial 7: Continuous model

The continuous Bayesian network used in this tutorial focuses on a fragment of a forced air heating and coolingsystem. In order to improve the system's efficiency, return air is mixed with the air that is drawn from outside.
Temperature of the outside air depends on the weather and is specified by means of a Normal distribution.
Return air temperature is constant and depends on the thermostat setting. Damper control signal determines
the composition of the mixture.

Temperature of the mixture is calculated according to the following equation: tma=toa*u_d+(tra-tra*u_d),where tma is mixed air temperature, toa is outside air temperature, u_d is the damper signal, and tra is return
air temperature.
The nodes in this model are created by the helper function createEquationNode. It is a modified version of thecreateCptNode function used in the previous tutorials. The bold text marks the difference between two
functions.
static int createEquationNode(
Network net, String id, String name,
String equation, double loBound, double hiBound,int xPos, int yPos) {int handle = net.addNode(Network.NodeType.EQUATION, id);
net.setNodeName(handle, name);
net.setNodeEquation(handle, equation);net.setNodeEquationBounds(handle, loBound, hiBound);net.setNodePosition(handle, xPos, yPos, 85, 55);
return handle;}
Instead of discrete outcomes, we specify the node equation and the bounds for the node value. Another helper
function, setUniformIntervals, is used to define the discretization intervals. They will be used by the inference
algorithm when the evidence is set for Mixed Air Temperature node (which has parents). The uniform intervals
are chosen for simplicity here; in general case the choice of interval edges should be done based on the actual
expected distribution of the node value (for example, in case of the Normal distribution, we might create narrowdiscretization intervals close to the mean.)

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

134

Tutorials
Note that while the model has three arcs, there are no calls to Network.addArc in this tutorial. The arcs are
created implicitly by Network.setNodeEquation method (called by createEquationNode function).
The network is complete now and we can proceed to inference. Three inference calls are made, one without
evidence and two with continuous evidence specified by calling Network.setContEvidence. Setting the Outside
Air Temperature to 28.5 degrees (toa is the name of int variable holding the handle of the Outside Air
Temperature node):Java:
net.setContEvidence(toa, 28.5);
Python:net.set_cont_evidence(toa, 28.5)
C#:
net.SetContEvidence(toa, 28.5);
The program uses updateAndShowStats helper function for inference. The helper calls
Network.updateBeliefs and iterates over the nodes in the network and calls another helper, showStats, foreach node. showStats first checks if the node has evidence set. If it does, the evidence value is printed, and thefunction returns. If node has no evidence, we need to check if its value comes from the sampling or discretized
inference.
If discretization was used, Network.isValueDiscretized returns true. In such case, the array returned from
Network.getNodeValue contains the probability distribution over node discretization intervals. To display
human-readable information about the intervals and probabilities, showStats calls
Network.getNodeEquationDiscretization and getNodeEquationBounds. Note that the program setup
these node attributes in createEquationNode.
If node value was sampled, Network.isValueDiscretized returns false. Network.getNodeValue returns thearray of sampled node values. After sampling, it's also possible to obtain sample statistics (mean, standard
deviation, min and max values) with a call to Network.getNodeSampleStats.
At the end of the tutorial, the model is saved to disk. Tutorial 8 143 will expand it into a hybrid network byadding CPT nodes.

7.7.1

Tutorial7.java

package tutorials;
import smile.*;// Tutorial7 creates a network with three equation-based nodes// performs the inference, then saves the model to disk.
public class Tutorial7 {public static void run() {System.out.println("Starting Tutorial7...");
Network net = new Network();net.setOutlierRejectionEnabled(true);createEquationNode(net,

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

135

Tutorials"tra", "Return Air Temperature","tra=24", 23.9, 24.1,
280, 100);
createEquationNode(net,"u_d", "Damper Control Signal","u_d = Bernoulli(0.539)*0.8 + 0.2", 0, 1,
160, 100);
int toa = createEquationNode(net,"toa", "Outside Air Temperature","toa=Normal(11,15)", -10, 40,
60, 100);// tra, toa and u_d are referenced in equation// arcs are created automatically
int tma = createEquationNode(net,"tma", "Mixed Air Temperature","tma=toa*u_d+(tra-tra*u_d)", 10, 30,
110, 200);
setUniformIntervals(net, toa, 5);
setUniformIntervals(net, tma, 4);
System.out.println("Results with no evidence:");updateAndShowStats(net);
net.setContEvidence(toa, 28.5);
System.out.println("Results with outside air temperature set to 28.5:");
updateAndShowStats(net);
net.clearEvidence(toa);
System.out.println("Results with mixed air temperature set to 21:");
net.setContEvidence(tma, 21.0);
updateAndShowStats(net);
net.writeFile("tutorial7.xdsl");
System.out.println("Tutorial7 complete: Network written to tutorial7.xdsl");}static int createEquationNode(
Network net, String id, String name,
String equation, double loBound, double hiBound,int xPos, int yPos) {int handle = net.addNode(Network.NodeType.EQUATION, id);
net.setNodeName(handle, name);
net.setNodeEquation(handle, equation);net.setNodeEquationBounds(handle, loBound, hiBound);net.setNodePosition(handle, xPos, yPos, 85, 55);
return handle;}
static void showStats(Network net, int nodeHandle) {SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

136

Tutorials
String nodeId = net.getNodeId(nodeHandle);
if (net.isEvidence(nodeHandle)) {double v = net.getContEvidence(nodeHandle);
System.out.printf("%s has evidence set (%g)\n", nodeId, v);
return;}
if (net.isValueDiscretized(nodeHandle)) {System.out.printf("%s is discretized.\n", nodeId);
DiscretizationInterval[] iv =net.getNodeEquationDiscretization(nodeHandle);
double[] bounds = net.getNodeEquationBounds(nodeHandle);
double[] discBeliefs = net.getNodeValue(nodeHandle);
double lo = bounds[0];for (int i = 0; i < discBeliefs.length; i++) {double hi = iv[i].boundary;
System.out.printf("\tP(%s in %g..%g)=%g\n", nodeId, lo, hi, discBeliefs[i]);lo = hi;}} else {double[] stats = net.getNodeSampleStats(nodeHandle);
System.out.printf("%s: mean=%g stddev=%g min=%g max=%g\n",nodeId, stats[0], stats[1], stats[2], stats[3]);}}static void setUniformIntervals(Network net, int nodeHandle, int count) {double[] bounds = net.getNodeEquationBounds(nodeHandle);
double lo = bounds[0];double hi = bounds[1];DiscretizationInterval[] iv = new DiscretizationInterval[count];for (int i = 0; i < count; i++) {iv[i] = new DiscretizationInterval(
null, lo + (i + 1) * (hi - lo) / count);}
net.setNodeEquationDiscretization(nodeHandle, iv);}
static void updateAndShowStats(Network net) {net.updateBeliefs();for (int h = net.getFirstNode(); h >= 0; h = net.getNextNode(h)){
showStats(net, h);}System.out.println();}}

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

Tutorials
7.7.2

137

Tutorial7.py

import pysmile# Tutorial7 creates a network with three equation-based nodes# performs the inference, then saves the model to disk.
class Tutorial7:
def __init__(self):
print("Starting Tutorial7...")
net = pysmile.Network()
net.set_outlier_rejection_enabled(True)
self.create_equation_node(net,"tra", "Return Air Temperature","tra=24", 23.9, 24.1, 280, 100)
self.create_equation_node(net,"u_d", "Damper Control Signal","u_d=Bernoulli(0.539)*0.8+0.2", 0, 1, 160, 100)
toa = self.create_equation_node(net,"toa", "Outside Air Temperature","toa=Normal(11,15)", -10, 40, 60, 100)
# tra, toa and u_d are referenced in equation# arcs are created automaticallytma = self.create_equation_node(net,"tma", "Mixed Air Temperature","tma=toa*u_d+(tra-tra*u_d)", 10, 30, 110, 200)
self.set_uniform_intervals(net, toa, 5)self.set_uniform_intervals(net, tma, 4)
print("Results with no evidence:")self.update_and_show_stats(net)net.set_cont_evidence(toa, 28.5)
print("Results with outside air temperature set to 28.5:")
self.update_and_show_stats(net)net.clear_evidence(toa)print("Results with mixed air temperature set to 21:")net.set_cont_evidence(tma, 21.0)
self.update_and_show_stats(net)net.write_file("tutorial7.xdsl")
print("Tutorial7 complete: Network written to tutorial7.xdsl")
def create_equation_node(self, net, id, name, equation, lo_bound,
hi_bound, x_pos, y_pos):handle = net.add_node(pysmile.NodeType.EQUATION, id)
net.set_node_name(handle, name)net.set_node_equation(handle, equation)

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

138

Tutorials
net.set_node_equation_bounds(handle, lo_bound, hi_bound)net.set_node_position(handle, x_pos, y_pos, 85, 55)
return handle
def show_stats(self, net, node_handle):
node_id = net.get_node_id(node_handle)
if net.is_evidence(node_handle):v = net.get_cont_evidence(node_handle)
print(f"{node_id} has evidence set: {v}")returnif net.is_value_discretized(node_handle):
print(f"{node_id} is discretized.")iv = net.get_node_equation_discretization(node_handle)
bounds = net.get_node_equation_bounds(node_handle)
disc_beliefs = net.get_node_value(node_handle)
lo = bounds[0]for i in range(0, len(disc_beliefs)):
hi = iv[i].boundary
print(f"\tP({node_id} in {lo}..{hi})={disc_beliefs[i]}")lo = hielse:stats = net.get_node_sample_stats(node_handle)
print(f"{node_id}: mean={stats[0]} stddev={stats[1]} min={stats[2]} max={stats[3]}")def set_uniform_intervals(self, net, node_handle, count):bounds = net.get_node_equation_bounds(node_handle)
lo = bounds[0]hi = bounds[1]iv = [None] * countfor i in range(0, count):iv[i] = pysmile.DiscretizationInterval("", lo + (i + 1) * (hi - lo)/ count)

net.set_node_equation_discretization(node_handle, iv)
def update_and_show_stats(self, net):
net.update_beliefs()
for h in net.get_all_nodes():self.show_stats(net, h)print()

7.7.3

Tutorial7.R

library(rSMILE)
source("License.R")# Tutorial7 creates a network with three equation-based nodes# performs the inference, then saves the model to disk.
createEquationNode = function(net, id, name, equation, loBound,hiBound, xPos, yPos) {SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

Tutorials

139

handle <- net$addNode(net$NodeType$EQUATION, id)
net$setNodeName(handle, name)net$setNodeEquation(handle, equation)
net$setNodeEquationBounds(handle, loBound, hiBound)
net$setNodePosition(handle, xPos, yPos, 85, 55)
return(handle)}
showStats = function(net, nodeHandle) {nodeId <- net$getNodeId(nodeHandle)
if (net$isEvidence(nodeHandle)) {v <- net$getContEvidence(nodeHandle)
cat(sprintf("%s has evidence set (%g)\n", nodeId, v))
return()}
if (net$isValueDiscretized(nodeHandle)) {cat(sprintf("%s is discretized.\n", nodeId))iv <- net$getNodeEquationDiscretization(nodeHandle)
bounds <- net$getNodeEquationBounds(nodeHandle)
discBeliefs <- net$getNodeValue(nodeHandle)
lo <- bounds[1]for(i in 0:(length(discBeliefs)-1)) {hi <- iv[[i+1]]$boundarycat(sprintf("\tP(%s in %g..%g)=%g\n",nodeId, lo, hi, discBeliefs[i+1]))
lo <- hi}} else {stats <- net$getNodeSampleStats(nodeHandle)
cat(sprintf("%s: mean=%g stddev=%g min=%g max=%g\n",nodeId, stats[1], stats[2], stats[3], stats[4]))}}setUniformIntervals = function(net, nodeHandle, count) {bounds <- net$getNodeEquationBounds(nodeHandle)
lo <- bounds[1]hi <- bounds[2]iv <- lapply(rep("DiscretizationInterval", count), new)num <- 1
for(i in iv) {i$id <- ""i$boundary <- lo + num * (hi - lo) / countnum <- num + 1}
net$setNodeEquationDiscretization(nodeHandle, iv)}
updateAndShowStats = function(net) {net$updateBeliefs()
nodes <- net$getAllNodes()for (h in nodes) {SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

Tutorials

140

showStats(net, h)}cat("\n")}cat("Starting Tutorial7...\n")
net <- Network()
net$setOutlierRejectionEnabled(TRUE)
createEquationNode(net, "tra", "Return Air Temperature","tra=24", 23.9, 24.1, 280, 100)
createEquationNode(net, "u_d", "Damper Control Signal","u_d = Bernoulli(0.539)*0.8 + 0.2", 0, 1, 160, 100)
toa <- createEquationNode(net, "toa", "Outside Air Temperature","toa=Normal(11,15)", -10, 40, 60, 100)
# tra, toa and u_d are referenced in equation# arcs are created automaticallytma <- createEquationNode(net, "tma", "Mixed Air Temperature","tma=toa*u_d+(tra-tra*u_d)", 10, 30, 110, 200)
setUniformIntervals(net, toa, 5)
setUniformIntervals(net, tma, 4)cat("Results with no evidence:\n")updateAndShowStats(net)
net$setContEvidence(toa, 28.5)
cat("Results with outside air temperature set to 28.5:\n")updateAndShowStats(net)
net$clearEvidence(toa)cat("Results with mixed air temperature set to 21:\n")net$setContEvidence(tma, 21.0)
updateAndShowStats(net)
net$writeFile("tutorial7.xdsl")
cat("Tutorial7 complete: Network written to tutorial7.xdsl\n")7.7.4

Tutorial7.cs

using System;
using Smile;// Tutorial7 creates a network with three equation-based nodes// performs the inference, then saves the model to disk.
namespace SmileNetTutorial{class Tutorial7{
public static void Run()

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

141

Tutorials{
Console.WriteLine("Starting Tutorial7...");
Network net = new Network();net.OutlierRejectionEnabled = true;
CreateEquationNode(net,"tra", "Return Air Temperature","tra=24", 23.9, 24.1,
280, 100);
CreateEquationNode(net,"u_d", "Damper Control Signal","u_d = Bernoulli(0.539)*0.8 + 0.2", 0, 1,
160, 100);
int toa = CreateEquationNode(net,"toa", "Outside Air Temperature","toa=Normal(11,15)", -10, 40,
60, 100);// tra, toa and u_d are referenced in equation// arcs are created automatically
int tma = CreateEquationNode(net,"tma", "Mixed Air Temperature","tma=toa*u_d+(tra-tra*u_d)", 10, 30,
110, 200);
SetUniformIntervals(net, toa, 5);
SetUniformIntervals(net, tma, 4);Console.WriteLine("Results with no evidence:");UpdateAndShowStats(net);
net.SetContEvidence(toa, 28.5);
Console.WriteLine("Results with outside air temperature set to 28.5:");
UpdateAndShowStats(net);
net.ClearEvidence(toa);
Console.WriteLine("Results with mixed air temperature set to 21:");
net.SetContEvidence(tma, 21.0);
UpdateAndShowStats(net);
net.WriteFile("tutorial7.xdsl");Console.WriteLine("Tutorial7 complete: Network written to tutorial7.xdsl");}private static int CreateEquationNode(
Network net, String id, String name,
String equation, double loBound, double hiBound,int xPos, int yPos){
int handle = net.AddNode(Network.NodeType.Equation, id);
net.SetNodeName(handle, name);
net.SetNodePosition(handle, xPos, yPos, 85, 55);

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

Tutorials

142

net.SetNodeEquation(handle, equation);
net.SetNodeEquationBounds(handle, loBound, hiBound);return handle;}
private static void ShowStats(Network net, int nodeHandle){
String nodeId = net.GetNodeId(nodeHandle);
if (net.IsEvidence(nodeHandle)){
double v = net.GetContEvidence(nodeHandle);
Console.WriteLine("{0} has evidence set ({1})", nodeId, v);
return;}
if (net.IsValueDiscretized(nodeHandle)){
Console.WriteLine("{0} is discretized.", nodeId);
DiscretizationInterval[] iv = net.GetNodeEquationDiscretization(nodeHandle);
double[] bounds = net.GetNodeEquationBounds(nodeHandle);
double[] discBeliefs = net.GetNodeValue(nodeHandle);
double lo = bounds[0];for (int i = 0; i < discBeliefs.Length; i++){double hi = iv[i].Boundary;Console.WriteLine("\tP({0} in {1}..{2})={3}",nodeId, lo, hi, discBeliefs[i]);lo = hi;}}else{
double[] stats = net.GetNodeSampleStats(nodeHandle);
Console.WriteLine("{0}: mean={1} stddev={2} min={3} max={4}",nodeId, stats[0], stats[1], stats[2], stats[3]);}}private static void SetUniformIntervals(
Network net, int nodeHandle, int count){double[] bounds = net.GetNodeEquationBounds(nodeHandle);
double lo = bounds[0];double hi = bounds[1];DiscretizationInterval[] iv = new DiscretizationInterval[count];for (int i = 0; i < count; i++){iv[i] = new DiscretizationInterval(null, lo + (i + 1) * (hi - lo) / count);}
net.SetNodeEquationDiscretization(nodeHandle, iv);}

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

143

Tutorials
private static void UpdateAndShowStats(Network net){
net.UpdateBeliefs();for (int h = net.GetFirstNode(); h >= 0; h = net.GetNextNode(h)){
ShowStats(net, h);}Console.WriteLine();
}
}
}

7.8

Tutorial 8: Hybrid model

We extend the model described in Tutorial 7 133 by adding two discrete nodes: Climate Zone and Perceived
Temperature. Climate Zone refines the probability distribution of the outside air temperature and Perceived
Temperature is an additional input originating from a subjective perception of the temperature, useful in case ofa failure in the outside temperature sensor.

After loading the network created in the previous tutorial, the program adds two discrete nodes using theCreateCptNode helper function first seen in Tutorial 1 82 . The arc from Climate Zone (node identifier is zone)
to Outside Air Temperature (node identifier is toa) is created by changing the equation of the latter:Java:
net.setNodeEquation(toaHandle, "toa=If(zone=\"Desert\",Normal(22,5),Normal(11,10))");
Python:net.set_node_equation("toa", 'toa=If(zone="Desert",Normal(22,5),Normal(11,10))')C#:
net.SetNodeEquation(toaHandle, "toa=If(zone=\"Desert\",Normal(22,5),Normal(11,10))");

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

144

Tutorials
In the Java and C# code, we need to escape the double quote characters in order to use the text literalrepresenting the Desert outcome of the parent. Python allows for single quotes to be used as string literaldelimiter.
The new equation switches between two normal distribution based on the outcome of the parent. The equation
could be also written in the following ways, all being functionally identical (assuming that Climate Zone has twooutcomes, Temperate and Desert):
toa=zone="Desert" ? Normal(22,5) : Normal(11,10)
toa=zone="Temperate" ? Normal(11,10) : Normal(22,5)
toa=Switch(zone, "Desert",Normal(22,5),"Temperate",Normal(11,10))
toa=Choose(zone,Normal(11,10),Normal(22,5))
To create the arc from Outside Air Temperature to Perceived Temperature the program calls Network.addArc.
The model loaded from disk had 5 discretization intervals already defined for Outside Air Temperature. With 5
possible discretized outcomes of its single parent and its own 3 outcomes, the CPT for Perceived Temperature
has 3*5=15 entries. It is initialized by a call to Network.setNodeDefinition with an appropriately sized array as
input. Note that for simplicity we do not change the default uniform distribution for the binary Climate Zone
node.
The program performs inference for each of the Climate Zone outcomes set as evidence. The output displayed
for the Outside Air Temperature node (toa) shows changing mean and standard deviation. Finally, the network
is saved to disk. This concludes the tutorial.

7.8.1

Tutorial8.java

package tutorials;
import smile.*;// Tutorial8 loads continuous model from the XDSL file written by Tutorial7,// then adds discrete nodes to create a hybrid model. Inference is performed// and model is saved to disk.
public class Tutorial8 {public static void run() {System.out.println("Starting Tutorial8...");
Network net = new Network();net.readFile("tutorial7.xdsl");createCptNode(net, "zone", "Climate Zone",
new String[] { "Temperate", "Desert" },60, 20);
int toaHandle = net.getNode("toa");
net.setNodeEquation(toaHandle, "toa=If(zone=\"Desert\",Normal(22,5),Normal(11,10))");
int perceivedHandle = createCptNode(net, "perceived", "Perceived Temperature",
new String[] { "Hot", "Warm", "Cold" },60, 300);
net.addArc(toaHandle, perceivedHandle);

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

145

Tutorials
double[] perceivedProbs = new double[] {0,// P(perceived=Hot |toa in -10..0)0.02, // P(perceived=Warm|toa in -10..0)0.98, // P(perceived=Cold|toa in -10..0)0.05, // P(perceived=Hot |toa in 0..10)
0.15, // P(perceived=Warm|toa in 0..10)
0.80, // P(perceived=Cold|toa in 0..10)
0.10, // P(perceived=Hot |toa in 10..20)
0.80, // P(perceived=Warm|toa in 10..20)
0.10, // P(perceived=Cold|toa in 10..20)
0.80, // P(perceived=Hot |toa in 20..30)
0.15, // P(perceived=Warm|toa in 20..30)
0.05, // P(perceived=Cold|toa in 20..30)
0.98, // P(perceived=Hot |toa in 30..40)
0.02, // P(perceived=Warm|toa in 30..40)0// P(perceived=Cold|toa in 30..40)};
net.setNodeDefinition(perceivedHandle, perceivedProbs);
net.setEvidence("zone", "Temperate");System.out.println("Results in temperate zone:");updateAndShowStats(net);
net.setEvidence("zone", "Desert");
System.out.println("Results in desert zone:\n");updateAndShowStats(net);
net.writeFile("tutorial8.xdsl");
System.out.println("Tutorial8 complete: Network written to tutorial8.xdsl");}static void showStats(Network net, int nodeHandle) {String nodeId = net.getNodeId(nodeHandle);
if (net.isEvidence(nodeHandle)) {double v = net.getContEvidence(nodeHandle);
System.out.printf("%s has evidence set (%g)\n", nodeId, v);
return;}
if (net.isValueDiscretized(nodeHandle)) {System.out.printf("%s is discretized.\n", nodeId);
DiscretizationInterval[] iv =net.getNodeEquationDiscretization(nodeHandle);
double[] bounds = net.getNodeEquationBounds(nodeHandle);
double[] discBeliefs = net.getNodeValue(nodeHandle);
double lo = bounds[0];for (int i = 0; i < discBeliefs.length; i++) {double hi = iv[i].boundary;
System.out.printf("\tP(%s in %g..%g)=%g\n", nodeId, lo, hi, discBeliefs[i]);lo = hi;}} else {SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

146

Tutorials
double[] stats = net.getNodeSampleStats(nodeHandle);
System.out.printf("%s: mean=%g stddev=%g min=%g max=%g\n",nodeId, stats[0], stats[1], stats[2], stats[3]);}}static void updateAndShowStats(Network net) {net.updateBeliefs();for (int h = net.getFirstNode(); h >= 0; h = net.getNextNode(h)){
if (net.getNodeType(h) == Network.NodeType.EQUATION) {showStats(net, h);}}System.out.println();}private static int createCptNode(
Network net, String id, String name,
String[] outcomes, int xPos, int yPos) {int handle = net.addNode(Network.NodeType.CPT, id);
net.setNodeName(handle, name);
net.setNodePosition(handle, xPos, yPos, 85, 55);
int initialOutcomeCount = net.getOutcomeCount(handle);for (int i = 0; i < initialOutcomeCount; i ++) {net.setOutcomeId(handle, i, outcomes[i]);}for (int i = initialOutcomeCount; i < outcomes.length; i ++) {net.addOutcome(handle, outcomes[i]);}return handle;
}
}

7.8.2

Tutorial8.py

import pysmile# Tutorial8 loads continuous model from the XDSL file written by Tutorial7,# then adds discrete nodes to create a hybrid model. Inference is performed# and model is saved to disk.
class Tutorial8:
def __init__(self):
print("Starting Tutorial8...")
net = pysmile.Network()
net.read_file("tutorial7.xdsl")
self.create_cpt_node(net,"zone", "Climate Zone",

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

147

Tutorials["Temperate", "Desert" ],60, 20)
toa_handle = net.get_node("toa")
net.set_node_equation("toa", 'toa=If(zone="Desert",Normal(22,5),Normal(11,10))')perceived_handle = self.create_cpt_node(net,"perceived", "Perceived Temperature",["Hot", "Warm", "Cold"],
60, 300)
net.add_arc(toa_handle, perceived_handle)
perceived_probs = [0,# P(perceived=Hot |toa in -10..0)0.02, # P(perceived=Warm|toa in -10..0)0.98, # P(perceived=Cold|toa in -10..0)0.05, # P(perceived=Hot |toa in 0..10)
0.15, # P(perceived=Warm|toa in 0..10)
0.80, # P(perceived=Cold|toa in 0..10)
0.10, # P(perceived=Hot |toa in 10..20)
0.80, # P(perceived=Warm|toa in 10..20)
0.10, # P(perceived=Cold|toa in 10..20)
0.80, # P(perceived=Hot |toa in 20..30)
0.15, # P(perceived=Warm|toa in 20..30)
0.05, # P(perceived=Cold|toa in 20..30)
0.98, # P(perceived=Hot |toa in 30..40)
0.02, # P(perceived=Warm|toa in 30..40)0# P(perceived=Cold|toa in 30..40)]
net.set_node_definition(perceived_handle, perceived_probs)
net.set_evidence("zone", "Temperate")
print("Results in temperate zone:")self.update_and_show_stats(net)net.set_evidence("zone", "Desert")
print("Results in desert zone:")self.update_and_show_stats(net)net.write_file("tutorial8.xdsl")
print("Tutorial8 complete: Network written to tutorial8.xdsl")
def show_stats(self, net, node_handle):
node_id = net.get_node_id(node_handle)
if net.is_evidence(node_handle):v = net.get_cont_evidence(node_handle)
print(f"{node_id} has evidence set: {v}")returnif net.is_value_discretized(node_handle):
print(node_id + " is discretized.")iv = net.get_node_equation_discretization(node_handle)
bounds = net.get_node_equation_bounds(node_handle)

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

Tutorials

148

disc_beliefs = net.get_node_value(node_handle)
lo = bounds[0]for i in range(0, len(disc_beliefs)):
hi = iv[i].boundary
print(f"\tP({node_id} in {lo}..{hi})={disc_beliefs[i]}")lo = hielse:stats = net.get_node_sample_stats(node_handle)
print(f"{node_id}: mean={stats[0]} stddev={stats[1]} min={stats[2]} max={stats[3]}")def update_and_show_stats(self, net):
net.update_beliefs()
for h in net.get_all_nodes():
if net.get_node_type(h) == pysmile.NodeType.EQUATION:
self.show_stats(net, h)print()
def create_cpt_node(self, net, id, name, outcomes, x_pos, y_pos):handle = net.add_node(pysmile.NodeType.CPT, id)
net.set_node_name(handle, name)net.set_node_position(handle, x_pos, y_pos, 85, 55)
initial_outcome_count = net.get_outcome_count(handle)
for i in range(0, initial_outcome_count):
net.set_outcome_id(handle, i, outcomes[i])for i in range(initial_outcome_count, len(outcomes)):
net.add_outcome(handle, outcomes[i])return handle

7.8.3

Tutorial8.R

library(rSMILE)
source("License.R")# Tutorial8 loads continuous model from the XDSL file written by Tutorial7,# then adds discrete nodes to create a hybrid model. Inference is performed# and model is saved to disk.
createCptNode = function(net, id, name, outcomes, xPos, yPos) {handle <- net$addNode(18, id)
initialOutcomeCount <- net$getOutcomeCount(handle)
count <- length(outcomes)
net$setNodeName(handle, name)net$setNodePosition(handle, xPos, yPos, 85L, 55L)
if (!is.null(outcomes)) {sapply(0:(initialOutcomeCount-1),function(x) net$setOutcomeId(handle, x, outcomes[x+1]))
if (initialOutcomeCount < count) {sapply(initialOutcomeCount:(count-1),function(x) net$addOutcome(handle, outcomes[x+1]))}

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

149

Tutorials}
return(handle)}
showStats = function(net, nodeHandle) {nodeId <- net$getNodeId(nodeHandle)
if (net$isEvidence(nodeHandle)) {v <- net$getContEvidence(nodeHandle)
cat(sprintf("%s has evidence set (%g)\n", nodeId, v))
return()}
if (net$isValueDiscretized(nodeHandle)) {cat(sprintf("%s is discretized.\n", nodeId))iv <- net$getNodeEquationDiscretization(nodeHandle)
bounds <- net$getNodeEquationBounds(nodeHandle)
discBeliefs <- net$getNodeValue(nodeHandle)
lo <- bounds[1]for(i in 0:(length(discBeliefs)-1)) {hi <- iv[[i+1]]$boundarycat(sprintf("\tP(%s in %g..%g)=%g\n",nodeId, lo, hi, discBeliefs[i+1]))
lo <- hi}} else {stats <- net$getNodeSampleStats(nodeHandle)
cat(sprintf("%s: mean=%g stddev=%g min=%g max=%g\n",nodeId, stats[1], stats[2], stats[3], stats[4]))}}updateAndShowStats = function(net) {net$updateBeliefs()
nodes <- net$getAllNodes()for (h in nodes) {if(net$getNodeType(h) == net$NodeType$EQUATION) {showStats(net, h)}}cat("\n")}cat("Starting Tutorial8...\n")
net <- Network()
net$readFile("tutorial7.xdsl")
createCptNode(net, "zone", "Climate Zone", c("Temperate", "Desert"), 60, 20)
toaHandle <- net$getNode("toa")
net$setNodeEquation(toaHandle, "toa=If(zone=\"Desert\",Normal(22,5),Normal(11,10))")perceivedHandle <- createCptNode(net, "perceived", "Perceived Temperature",c("Hot", "Warm", "Cold"), 60, 300)

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

150

Tutorials
net$addArc(toaHandle, perceivedHandle)
perceivedProbs = c( 0.00,
0.02,
0.98,
0.05,
0.15,
0.80,
0.10,
0.80,
0.10,
0.80,
0.15,
0.05,
0.98,
0.02,
0.00 )
net$setNodeDefinition(perceivedHandle, perceivedProbs)
net$setEvidence("zone", "Temperate")cat("Results in temperate zone:\n")updateAndShowStats(net)
net$setEvidence("zone", "Desert")
cat("Results in desert zone:\n")updateAndShowStats(net)
net$writeFile("tutorial8.xdsl")
cat("Tutorial8 complete: Network written to tutorial8.xdsl\n")7.8.4

Tutorial8.cs

using System;
using Smile;// Tutorial8 loads continuous model from the XDSL file written by Tutorial7,// then adds discrete nodes to create a hybrid model. Inference is performed// and model is saved to disk.
namespace SmileNetTutorial{class Tutorial8{
public static void Run(){
Console.WriteLine("Starting Tutorial8...");
Network net = new Network();
net.ReadFile("tutorial7.xdsl");CreateCptNode(net, "zone", "Climate Zone",

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

151

Tutorials
new String[] { "Temperate", "Desert" },60, 20);
int toaHandle = net.GetNode("toa");
net.SetNodeEquation(toaHandle,"toa=If(zone=\"Desert\",Normal(22,5),Normal(11,10))");
int perceivedHandle = CreateCptNode(net, "perceived", "Perceived Temperature",
new String[] { "Hot", "Warm", "Cold" },60, 300);
net.AddArc(toaHandle, perceivedHandle);
double[] perceivedProbs = new double[] {0,// P(perceived=Hot |toa in -10..0)0.02, // P(perceived=Warm|toa in -10..0)0.98, // P(perceived=Cold|toa in -10..0)0.05, // P(perceived=Hot |toa in 0..10)
0.15, // P(perceived=Warm|toa in 0..10)
0.80, // P(perceived=Cold|toa in 0..10)
0.10, // P(perceived=Hot |toa in 10..20)
0.80, // P(perceived=Warm|toa in 10..20)
0.10, // P(perceived=Cold|toa in 10..20)
0.80, // P(perceived=Hot |toa in 20..30)
0.15, // P(perceived=Warm|toa in 20..30)
0.05, // P(perceived=Cold|toa in 20..30)
0.98, // P(perceived=Hot |toa in 30..40)
0.02, // P(perceived=Warm|toa in 30..40)0// P(perceived=Cold|toa in 30..40)};
net.SetNodeDefinition(perceivedHandle, perceivedProbs);
net.SetEvidence("zone", "Temperate");Console.WriteLine("Results in temperate zone:");UpdateAndShowStats(net);
net.SetEvidence("zone", "Desert");Console.WriteLine("Results in desert zone:\n");UpdateAndShowStats(net);
net.WriteFile("tutorial8.xdsl");Console.WriteLine("Tutorial8 complete: Network written to tutorial8.xdsl");}private static int CreateEquationNode(
Network net, String id, String name,
String equation, double loBound, double hiBound,int xPos, int yPos){
int handle = net.AddNode(Network.NodeType.Equation, id);
net.SetNodeName(handle, name);
net.SetNodePosition(handle, xPos, yPos, 85, 55);
net.SetNodeEquation(handle, equation);
net.SetNodeEquationBounds(handle, loBound, hiBound);return handle;

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

152

Tutorials}
private static void ShowStats(Network net, int nodeHandle){
String nodeId = net.GetNodeId(nodeHandle);
if (net.IsEvidence(nodeHandle)){
double v = net.GetContEvidence(nodeHandle);
Console.WriteLine("{0} has evidence set ({1})", nodeId, v);
return;}
if (net.IsValueDiscretized(nodeHandle)){
Console.WriteLine("{0} is discretized.", nodeId);
DiscretizationInterval[] iv =net.GetNodeEquationDiscretization(nodeHandle);
double[] bounds = net.GetNodeEquationBounds(nodeHandle);
double[] discBeliefs = net.GetNodeValue(nodeHandle);
double lo = bounds[0];for (int i = 0; i < discBeliefs.Length; i++){double hi = iv[i].Boundary;Console.WriteLine("\tP({0} in {1}..{2})={3}",nodeId, lo, hi, discBeliefs[i]);lo = hi;}}else{
double[] stats = net.GetNodeSampleStats(nodeHandle);
Console.WriteLine("{0}: mean={1} stddev={2} min={3} max={4}",nodeId, stats[0], stats[1], stats[2], stats[3]);}}private static void UpdateAndShowStats(Network net){
net.UpdateBeliefs();for (int h = net.GetFirstNode(); h >= 0; h = net.GetNextNode(h)){
if (net.GetNodeType(h) == (int)Network.NodeType.Equation){
ShowStats(net, h);}}Console.WriteLine();}private static int CreateCptNode(
Network net, String id, String name,
String[] outcomes, int xPos, int yPos){
int handle = net.AddNode(Network.NodeType.Cpt, id);

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

153

Tutorials
net.SetNodeName(handle, name);
net.SetNodePosition(handle, xPos, yPos, 85, 55);
int initialOutcomeCount = net.GetOutcomeCount(handle);for (int i = 0; i < initialOutcomeCount; i++){net.SetOutcomeId(handle, i, outcomes[i]);}for (int i = initialOutcomeCount; i < outcomes.Length; i++){net.AddOutcome(handle, outcomes[i]);}return handle;
}
}
}

7.9

Tutorial 9: Diagnosis

The program in this section uses one of the example discrete networks distributed in GeNIe, which can be
downloaded directly using the following link: https://support.bayesfusion.com/docs/Examples/Discrete%
20Bayesian%20Networks/HeparII.xdsl. The model is also available in BayesFusion's online repository at
https://repo.bayesfusion.com/network/permalink?net=Small+BNs%2FHeparII.xdsl.

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

154

Tutorials
HeparII.xdsl is a Bayesian network model for the diagnosis of liver disorders. It has diagnostic roles defined for
its nodes. The model nodes were formatted to use yellow as a background for fault nodes, and green/blue forobservation nodes. Note that while binary fault nodes have one fault state (present), there are also two fault
nodes with three states. These nodes have two out of three states marked as faulty, like active and persistent in
the case of Chronic Hepatitis.
Our program loads the network at startup and calls its helper function printDiagTypes to output the
information about diagnostic roles. The first lines of the output look like this:Node Id
alcoholism
vh_amnhepatotoxic
THepatitis
hospital
surgery
gallstones
choledocholithotomy
injectionstransfusion
ChHepatitis
sex...

Diagnostic Type
NodeDiagType.OBSERVATION
NodeDiagType.OBSERVATION
NodeDiagType.OBSERVATION
NodeDiagType.FAULT
NodeDiagType.OBSERVATION
NodeDiagType.OBSERVATION
NodeDiagType.OBSERVATION
NodeDiagType.OBSERVATION
NodeDiagType.OBSERVATION
NodeDiagType.OBSERVATION
NodeDiagType.FAULT
NodeDiagType.OBSERVATION

Fault Outcomes
n/a
n/a
n/a
['present']
n/a
n/a
n/a
n/a
n/a
n/a
['active', 'persistent']
n/a

The printDiagTypes function iterates over the nodes in the network. Its main loop calls
Network.getNodeDiagType to determine the diagnostic role. If the node is a fault, its outcomes are also
inspected using Network.isFaultOutcome.
Java:
int diagType = net.getNodeDiagType(nodeId);
List<String> faultOutcomes = new ArrayList<>();
if (diagType == Network.NodeDiagType.FAULT) {for (String outcomeId : net.getOutcomeIds(nodeId)) {if (net.isFaultOutcome(nodeId, outcomeId)) {faultOutcomes.add(outcomeId);}}}
Python:diag_type = net.get_node_diag_type(node_id)
fault_outcomes = []if diag_type == pysmile.NodeDiagType.FAULT:
for outcome_id in net.get_outcome_ids(node_id):
if net.is_fault_outcome(node_id, outcome_id):
fault_outcomes.append(outcome_id)
C#:
Network.NodeDiagType diagType = net.GetNodeDiagType(nodeId);
List<string> faultOutcomes = new List<string>();if (diagType == Network.NodeDiagType.Fault){
foreach (string outcomeId in net.GetOutcomeIds(nodeId)){if (net.IsFaultOutcome(nodeId, outcomeId)){

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

155

Tutorials
faultOutcomes.Add(outcomeId);}}}
We now proceed to the actual diagnosis: the DiagNetwork object is instantiated. In its constructor, the fault
probabilities are calculated and the most likely fault is selected to be pursued. DiagNetwork uses zero-based
integer indices to identify faults (node/node outcome pairs). The printFaultIndices helper function iterates
over faults. It uses DiagNetwork.getFaultCount, getFaultNodeId and getFaultOutcomeId to emit thefollowing output:
Diagnostic network has 11 faults (node/outcome pairs)
Fault Index Fault Node Id
Fault Outcome Id
0
THepatitis
present1
ChHepatitis
active2
ChHepatitis
persistent3
PBC
present
4
fibrosispresent
5
Steatosis
present
6
Cirrhosis
decompensate
7
Cirrhosis
compensate
8Hyperbilirubinemia
present
9
RHepatitis
present
10
carcinomapresent
We can read the index of the currently pursued fault with DiagNetwork.getPursuedFault. Initially, the most
likely fault is at index 3, which represents the node PBC and its outcome present.
Java:
int pursuedFaultIdx = diag.getPursuedFault();
System.out.printf("The default (most likely) pursued fault is at index %d: %s=%s\n",pursuedFaultIdx, diag.getFaultNodeId(pursuedFaultIdx),
diag.getFaultOutcomeId(pursuedFaultIdx));
Python:pursued_fault_idx = diag.get_pursued_fault()
print(f"The default (most likely) pursued fault is at index {pursued_fault_idx}: "+ f"{diag.get_fault_node_id(pursued_fault_idx)}"+ f"={diag.get_fault_outcome_id(pursued_fault_idx)}")C#:
int pursuedFaultIdx = diag.GetPursuedFault();
Console.WriteLine("The default (most likely) pursued fault is at index {0}: {1}={2}",pursuedFaultIdx,
diag.GetFaultNodeId(pursuedFaultIdx), diag.GetFaultOutcomeId(pursuedFaultIdx));
The program performs the diagnostic update without any observations instantiated. DiagNetwork.update iscalled, and results are returned as a DiagResults object. The diagnostic results contain two arrays, one forfaults, and another for observations. The arrays are sorted: faults are sorted by probability with most likely faults
at the top, and observations are sorted by the magnitude of the information gain. Our printDiagResults
iterates over the elements of these arrays. printFaultInfo and printObservationInfo are used to access themembers of the FaultInfo and ObservationInfo objects, respectively. The first lines of the fault/observation
output look like this:Faults:Node Id
PBC

SMILE Wrappers

Outcome Id
present

Probability
0.41341570632059377

Is Pursued
Yes

Version 2.2.4.R1, Built on 4/27/2024

156

Tutorials
ChHepatitis
Steatosis
carcinoma...
Observations:
Node Idama
cholesterol
ESR
bilirubin
age
skin
...

activepresent
present

Measure0.5576308476489384
0.4868445142223191
0.42960779128083926
0.41580785350162774-0.33803891617697684
0.3100428612068602

0.1290048962276199
0.09585221239536176
0.0665709760500613

Cost
0.0
0.0
0.0
0.0
0.0
0.0

No
No
No

InfoGain
0.5576308476489384
0.4868445142223191
0.42960779128083926
0.41580785350162774-0.33803891617697684
0.3100428612068602

The default diagnostic measure algorithm for single fault diagnosis uses the maximum magnitude of probabilitychange in the pursued fault over all outcomes of the observation node. From the output, we can deduce that theama node has an outcome that causes the probability PBC=present to increase by 0.55763. Note also that age
infoGain is negative due to (signed) probability change used as a diagnostic measure algorithm. For brevity, thetutorial program does not use ObservationInfo.outcomeGain array, which contains per-outcome infoGain.
From outcomeGain, we could determine which outcome of the ama node is responsible for the maximum
change in PBC. Other members of ObservationInfo not displayed by this tutorial are faultPosteriors andobservationPriors. The faultPosteriors array contains probabilities of pursued fault after instantiatingeach outcome of the observation. The observationPriors array contains prior probabilities for eachobservation outcome.
We will now instantiate (observe) some observation nodes. DiagNetwork.instantiateObservation calls
Network.setEvidence internally, and updates DiagNetwork's state. Always call
DiagNetwork.instantiateObservation when the diagnostic session is in progress. Calling
Network.setEvidence directly may lead to inconsistent results or errors. Our observations are:
· jaundice=present· nausea=absent· obesity=present

The code is straightforward:Java:
diag.instantiateObservation("jaundice", "present");
diag.instantiateObservation("nausea", "absent");
diag.instantiateObservation("obesity", "present");
Python:diag.instantiate_observation("jaundice", "present")
diag.instantiate_observation("nausea", "absent")
diag.instantiate_observation("obesity", "present")
C#:
diag.InstantiateObservation("jaundice", "present");
diag.InstantiateObservation("nausea", "absent");
diag.InstantiateObservation("obesity", "present");
After another call to DiagNetwork.update and printDiagResults, we get the following output:
Faults:Node Id

SMILE Wrappers

Outcome Id

Probability

Is Pursued

Version 2.2.4.R1, Built on 4/27/2024

157

Tutorials
PBC
Steatosis
ChHepatitis
Cirrhosis...
Observations:
Node Idama
cholesterolbilirubin
ESR
age
skin
...

present
present
activedecompensate

Measure0.5940244757037065
0.4557094807486714
0.43194679397989816
0.41696920311719615-0.3062170075414388
0.2869860117172001

0.3716679804854865
0.20822898960092975
0.12911036437662063
0.08774271003265043

Cost
0.0
0.0
0.0
0.0
0.0
0.0

Yes
No
No
No

InfoGain
0.5940244757037065
0.4557094807486714
0.43194679397989816
0.41696920311719615-0.3062170075414388
0.2869860117172001

With known jaundice, nausea and obesity the fault probabilities and observation infoGains have changed, but
the ama node is still our most useful observation.
The next step is to change the outcome of obesity to absent and retract the observation of nausea. We will also
switch our pursued fault to Hyperbilirubinemia=present.
Java:
diag.releaseObservation("nausea");diag.instantiateObservation("obesity", "absent");
int hyperbilirubinemiaIdx = diag.getFaultIndex("Hyperbilirubinemia", "present");
diag.setPursuedFault(hyperbilirubinemiaIdx);
Python:diag.release_observation("nausea")diag.instantiate_observation("obesity", "absent")
hyperbilirubinemia_idx = diag.get_fault_index("Hyperbilirubinemia", "present")
diag.set_pursued_fault(hyperbilirubinemia_idx)
C#:diag.ReleaseObservation("nausea");diag.InstantiateObservation("obesity", "absent");
int hyperbilirubinemiaIdx = diag.GetFaultIndex("Hyperbilirubinemia", "present");
diag.SetPursuedFault(hyperbilirubinemiaIdx);
Another DiagNetwork.update and printDiagResults calls produce this output:
Node Id
PBC
ChHepatitis
Steatosis
carcinoma
ChHepatitis
Hyperbilirubinemia...
Observations:
Node Idage
hepatomegalybilirubin
ggtp
ESR
ama

SMILE Wrappers

Outcome Id
present
activepresent
present
persistentpresent

Measure0.2455407606076575
0.04617967333309004
0.042062826363683546-0.03279259884194631-0.024267251569646417-0.023463595723063817

Probability
0.3699731063429856
0.12884438248137664
0.08508721992530559
0.06083429718416463
0.05272028107403933
0.052337451916630945

Cost
0.0
0.0
0.0
0.0
0.0
0.0

Is Pursued
No
No
No
No
No
Yes

InfoGain
0.2455407606076575
0.04617967333309004
0.042062826363683546-0.03279259884194631-0.024267251569646417-0.023463595723063817

Version 2.2.4.R1, Built on 4/27/2024

158

Tutorials...
We can immediately notice that the major factor determining the probability of Hyperbilirubinemia=present isage. Of course in the real-world scenario, we would know the age of the patient before starting the diagnosis.
Our final diagnosis with a single pursued fault will switch to a cross-entropy measure for infoGain. We do notchange observations and the pursued faults remain the same.
Java:
diag.setSingleFaultAlgorithm(DiagNetwork.SingleFaultAlgorithmType.CROSSENTROPY);
Python:diag.set_single_fault_algorithm(pysmile.SingleFaultAlgorithmType.CROSSENTROPY)
C#:
diag.SingleFaultAlgorithm = DiagNetwork.SingleFaultAlgorithmType.Crossentropy;
The output from DiagNetwork.update now has non-negative infoGain for all observations. Note that fault
probabilities do not change when the diagnostic measure algorithm is changed, therefore the fragment of thetutorial output below omits the fault info.
Observations:
Node Idage
hepatomegaly
ggtp
bilirubinsex
pain_ruq
ESR
ama
hepatalgiacholesterol...

Measure0.05465458670852352
0.01315927809591283
0.010610204503726073
0.006204569424644146
0.0031695792371849163
0.00263781234873367
0.0026302997853497323
0.0025319015011320833
0.001572547559233084
9.548883878895409E-4

Cost
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0

InfoGain
0.05465458670852352
0.01315927809591283
0.010610204503726073
0.006204569424644146
0.0031695792371849163
0.00263781234873367
0.0026302997853497323
0.0025319015011320833
0.001572547559233084
9.548883878895409E-4

Without changing the observations, we now proceed to multi-fault diagnosis. We will keep
Hyperbilirubinemia=present and add Steatosis=present. We know the fault index for the first fault already, butneed to get the index for the second fault.
Java:
int steatosisIdx = diag.getFaultIndex("Steatosis", "present");
diag.setPursuedFaults(new int[]{steatosisIdx, hyperbilirubinemiaIdx});
Python:steatosis_idx = diag.get_fault_index("Steatosis", "present")
diag.set_pursued_faults([steatosis_idx, hyperbilirubinemia_idx])
C#:
int steatosisIdx = diag.GetFaultIndex("Steatosis", "present");
diag.SetPursuedFaults(new int[] { steatosisIdx, hyperbilirubinemiaIdx });After a DiagNetwork.update we will get identical fault probabilities (because observations did not change), but
the infoGains will be different:
Observations:
Node Id
triglycerides

SMILE Wrappers

Measure0.32730161296292315

Cost
0.0

InfoGain
0.32730161296292315

Version 2.2.4.R1, Built on 4/27/2024

159

Tutorials
agealcoholism
inrirregular_liver
spleenspiders
palms
edge
ast
alt
platelet...

0.2455407606076575
0.14812380191186358
0.13221077172053503
0.11230469436997036
0.09007368131876663
0.06621496955185409
0.06617286150395034
0.058990744385672464-0.056481838925186656-0.05257654040287
0.05126314745980294

0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0

0.2455407606076575
0.14812380191186358
0.13221077172053503
0.11230469436997036
0.09007368131876663
0.06621496955185409
0.06617286150395034
0.058990744385672464-0.056481838925186656-0.05257654040287
0.05126314745980294

The default measure algorithm for multi-fault diagnosis uses the maximum magnitude of probability changeover all faults and all observation outcomes. Our last diagnosis will keep the observations, but the measurealgorithm will be normalized Euclidean distance in the fault probability vector space.
Java:
diag.setMultiFaultAlgorithm(DiagNetwork.MultiFaultAlgorithmType.L2_NORMALIZED_DISTANCE);
Python:diag.set_multi_fault_algorithm(pysmile.MultiFaultAlgorithmType.L2_NORMALIZED_DISTANCE)C#:
diag.MultiFaultAlgorithm = DiagNetwork.MultiFaultAlgorithmType.L2NormalizedDistance;
Here's the observation output after DiagNetwork.update:Observations:
Node Id
age
ast
alt
triglycerides
inralcoholism
bilirubin
hepatomegaly
ggtp
cholesterol
irregular_liver
ESR
spleenalbumin...

Measure0.5131574568755791
0.36613951542960194
0.3263962662652687
0.29763021106578275
0.23813502209247214
0.23215767497663264
0.2217351591088665
0.21201689659082573
0.20797749804060328
0.20654898750297776
0.20400352015821183
0.19743075347810374
0.18236843341781642
0.18078914435171048

Cost
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0

InfoGain
0.5131574568755791
0.36613951542960194
0.3263962662652687
0.29763021106578275
0.23813502209247214
0.23215767497663264
0.2217351591088665
0.21201689659082573
0.20797749804060328
0.20654898750297776
0.20400352015821183
0.19743075347810374
0.18236843341781642
0.18078914435171048

This concludes tutorial 9.

7.9.1

Tutorial9.java

package tutorials;
import smile.*;import java.util.ArrayList;
import java.util.List;

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

160

Tutorials// Tutorial9 loads HeparII.xdsl file// and runs multiple diagnostic algorithms// Use the link below to download the HeparII.xdsl file:// https://support.bayesfusion.com/docs/Examples/Discrete%20Bayesian%20Networks/HeparII.xdsl
public class Tutorial9 {public static void run() {System.out.println("Starting diagnosis example");Network net = new Network();net.readFile("HeparII.xdsl");
System.out.println("Hepar model loaded");printDiagTypes(net);
System.out.println("Creating diagnostic network object");
DiagNetwork diag = new DiagNetwork(net);
printFaultIndices(diag);
int pursuedFaultIdx = diag.getPursuedFault();
System.out.printf("The default (most likely) pursued fault is at index %d: %s=%s\n",pursuedFaultIdx, diag.getFaultNodeId(pursuedFaultIdx),
diag.getFaultOutcomeId(pursuedFaultIdx));
System.out.println("Running diagnosis with no instantiated observations");
DiagResults diagResults = diag.update();printDiagResults(net, diagResults);
System.out.println("Running diagnosis with three observations");
diag.instantiateObservation("jaundice", "present");
diag.instantiateObservation("nausea", "absent");
diag.instantiateObservation("obesity", "present");
diagResults = diag.update();printDiagResults(net, diagResults);
System.out.println("Running diagnosis with two observations and "+ "focusing on Hyperbilirubinemia");diag.releaseObservation("nausea");diag.instantiateObservation("obesity", "absent");
int hyperbilirubinemiaIdx = diag.getFaultIndex("Hyperbilirubinemia", "present");
diag.setPursuedFault(hyperbilirubinemiaIdx);
diagResults = diag.update();printDiagResults(net, diagResults);
System.out.println("Switching algorithm to cross-entropy, "+ "observations and pursued fault unchanged");diag.setSingleFaultAlgorithm(DiagNetwork.SingleFaultAlgorithmType.CROSSENTROPY);
diagResults = diag.update();printDiagResults(net, diagResults);
System.out.println("Running diagnosis with two observations and "+ "focusing on both Hyperbilirubinemia and Steatosis");int steatosisIdx = diag.getFaultIndex("Steatosis", "present");
diag.setPursuedFaults(new int[]{steatosisIdx, hyperbilirubinemiaIdx});
diagResults = diag.update();printDiagResults(net, diagResults);

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

161

Tutorials

System.out.println("Swtiching algorithm to L2 distance, "+ "observations and pursued faults unchanged");diag.setMultiFaultAlgorithm(DiagNetwork.MultiFaultAlgorithmType.L2_NORMALIZED_DISTANCE);
diagResults = diag.update();printDiagResults(net, diagResults);
System.out.println("Tutorial9 complete.");}public static void printDiagTypes(Network net) {System.out.printf("Network has %d nodes\n", net.getNodeCount());
System.out.printf("%-20s %-30s %-30s\n","Node Id", "Diagnostic Type", "Fault Outcomes");
String[] nodeIds = net.getAllNodeIds();for (String nodeId : nodeIds) {int diagType = net.getNodeDiagType(nodeId);
List<String> faultOutcomes = new ArrayList<>();
if (diagType == Network.NodeDiagType.FAULT) {for (String outcomeId : net.getOutcomeIds(nodeId)) {if (net.isFaultOutcome(nodeId, outcomeId)) {faultOutcomes.add(outcomeId);}}}
System.out.printf("%-20s %-30s %-30s\n", nodeId, diagType,(faultOutcomes.size() != 0) ? String.join(", ", faultOutcomes) : "n/a");}}public static void printFaultIndices(DiagNetwork diag) {int faultCount = diag.getFaultCount();
System.out.printf("Diagnostic network has %d faults (node/outcome pairs)\n",faultCount);
System.out.printf("%-12s %-20s %-20s\n","Fault Index", "Fault Node Id", "Fault Outcome Id");for (int fIdx = 0; fIdx < faultCount; fIdx++) {System.out.printf("%-12d %-20s %-20s\n",fIdx, diag.getFaultNodeId(fIdx), diag.getFaultOutcomeId(fIdx));}}
public static void printFaultInfo(Network net, FaultInfo fault) {System.out.printf("%-20s %-20s %-24s %-8s\n",net.getNodeId(fault.node), net.getOutcomeId(fault.node, fault.outcome),
fault.probability, fault.isPursued ? "Yes" : "No");}public static void printObservationInfo(Network net, ObservationInfo observation) {System.out.printf("%-20s %-24s %-8s %-20s\n",net.getNodeId(observation.node), observation.measure,observation.cost, observation.infoGain);}public static void printDiagResults(Network net, DiagResults diagResults) {System.out.println("Diag results start\n\nFaults:");SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

162

Tutorials
System.out.printf("%-20s %-20s %-24s %-8s\n","Node Id", "Outcome Id", "Probability", "Is Pursued");for (FaultInfo faultInfo : diagResults.faults) {printFaultInfo(net, faultInfo);}
System.out.println("\nObservations:");System.out.printf("%-20s %-24s %-8s %-20s\n","Node Id", "Measure", "Cost", "InfoGain");for (ObservationInfo observationInfo : diagResults.observations) {printObservationInfo(net, observationInfo);}
System.out.println("\nDiag results end\n");
}
}

7.9.2

Tutorial9.py

import pysmile# Tutorial9 loads HeparII.xdsl file# and runs multiple diagnostic algorithms# Use the link below to download the HeparII.xdsl file:# https://support.bayesfusion.com/docs/Examples/Discrete%20Bayesian%20Networks/HeparII.xdsl
class Tutorial9:
def __init__(self):
print("Starting tutorial 9...")
net = pysmile.Network()
net.read_file("HeparII.xdsl")
print("Hepar model loaded")print_diag_types(net)
print("Creating diagnostic network object")diag = pysmile.DiagNetwork(net)
print_fault_indices(diag)pursued_fault_idx = diag.get_pursued_fault()
print(f"The default (most likely) pursued fault is at index {pursued_fault_idx}: "+ f"{diag.get_fault_node_id(pursued_fault_idx)}"+ f"={diag.get_fault_outcome_id(pursued_fault_idx)}")print("Running diagnosis with no instantiated observations")
diag_results = diag.update()
print_diag_results(net, diag_results)
print("Running diagnosis with three observations")
diag.instantiate_observation("jaundice", "present")
diag.instantiate_observation("nausea", "absent")
diag.instantiate_observation("obesity", "present")
diag_results = diag.update()
print_diag_results(net, diag_results)
print("Running diagnosis with two observations and focusing on Hyperbilirubinemia")
diag.release_observation("nausea")diag.instantiate_observation("obesity", "absent")

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

163

Tutorials
hyperbilirubinemia_idx = diag.get_fault_index("Hyperbilirubinemia", "present")
diag.set_pursued_fault(hyperbilirubinemia_idx)
diag_results = diag.update()
print_diag_results(net, diag_results)

print("Switching algorithm to cross-entropy, observations and pursued fault unchanged")diag.set_single_fault_algorithm(pysmile.SingleFaultAlgorithmType.CROSSENTROPY)
diag_results = diag.update()
print_diag_results(net, diag_results)
print("Running diagnosis with two observations and "+ "focusing on both Hyperbilirubinemia and Steatosis")steatosis_idx = diag.get_fault_index("Steatosis", "present")
diag.set_pursued_faults([steatosis_idx, hyperbilirubinemia_idx])
diag_results = diag.update()
print_diag_results(net, diag_results)
print("Swtiching algorithm to L2 distance, observations and pursued faults unchanged")diag.set_multi_fault_algorithm(pysmile.MultiFaultAlgorithmType.L2_NORMALIZED_DISTANCE)
diag_results = diag.update()
print_diag_results(net, diag_results)
print("Tutorial 9 complete.")def print_diag_types(net):
print(f"Network has {net.get_node_count()} nodes")print("{:<20} {:<30} {:<30}".format("Node Id", "Diagnostic Type", "Fault Outcomes"))
for node_id in net.get_all_node_ids():
diag_type = net.get_node_diag_type(node_id)
fault_outcomes = []if diag_type == pysmile.NodeDiagType.FAULT:
for outcome_id in net.get_outcome_ids(node_id):
if net.is_fault_outcome(node_id, outcome_id):
fault_outcomes.append(outcome_id)
print("{:<20} {:<30} {:<30}".format(node_id, str(net.get_node_diag_type(node_id)),str(fault_outcomes) if diag_type == pysmile.NodeDiagType.FAULT else "n/a"))def print_fault_indices(diag):
fault_count = diag.get_fault_count()
print(f"Diagnostic network has {fault_count} faults (node/outcome pairs)")
print("{:<12} {:<20} {:<20}".format("Fault Index", "Fault Node Id", "Fault Outcome Id"))
for fidx in range(fault_count):
print("{:<12} {:<20} {:<20}".format(
fidx, diag.get_fault_node_id(fidx), diag.get_fault_outcome_id(fidx)))
def print_fault_info(net, fault):
print("{:<20} {:<20} {:<24} {:<8}".format(
net.get_node_id(fault.node), net.get_outcome_id(fault.node, fault.outcome),
fault.probability, "Yes" if fault.is_pursued else "No"))def print_observation_info(net, observation):
print("{:<20} {:<24} {:<8} {:<20}".format(net.get_node_id(observation.node),
observation.measure, observation.cost, observation.info_gain))def print_diag_results(net, diag_results):

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

164

Tutorials
print("Diag results start\n\nFaults:")
print("{:<20} {:<20} {:<24} {:<8}".format("Node Id", "Outcome Id", "Probability", "Is Pursued"))for fault_info in diag_results.faults:
print_fault_info(net, fault_info)
print("\nObservations:")
print("{:<20} {:<24} {:<8} {:<20}".format("Node Id", "Measure", "Cost", "InfoGain"))for observation_info in diag_results.observations:print_observation_info(net, observation_info)
print("\nDiag results end\n")7.9.3

Tutorial9.R

library(rSMILE)
source("License.R")# Tutorial9 loads HeparII.xdsl file# and runs multiple diagnostic algorithms# Use the link below to download the HeparII.xdsl file:# https://support.bayesfusion.com/docs/Examples/Discrete%20Bayesian%20Networks/HeparII.xdsl
printDiagTypes <- function(net) {cat(sprintf("Network has %d nodes\n", net$getNodeCount()))
cat(sprintf("%-20s %-30s %-30s\n", "Node Id", "Diagnostic Type", "Fault Outcomes"))nodeIds <- net$getAllNodeIds()
for (nodeId in nodeIds) {diagType <- net$getNodeDiagType(nodeId)
faultOutcomes <- character()if (diagType == net$NodeDiagType$FAULT) {outcomeIds <- net$getOutcomeIds(nodeId)for (outcomeId in outcomeIds) {if (net$isFaultOutcome(nodeId, outcomeId)) {faultOutcomes <- c(faultOutcomes, outcomeId)}}}cat(sprintf("%-20s %-30s %-30s\n",nodeId, as.character(net$getNodeDiagType(nodeId)),
ifelse(diagType == net$NodeDiagType$FAULT,
paste(faultOutcomes, collapse = ", "), "n/a")))}}printFaultIndices <- function(diag) {faultCount <- diag$getFaultCount()cat(sprintf("Diagnostic network has %d faults (node/outcome pairs)\n", faultCount))cat(sprintf("%-12s %-20s %-20s\n", "Fault Index", "Fault Node Id", "Fault Outcome Id"))for (fIdx in 0:(faultCount-1)) {cat(sprintf("%-12d %-20s %-20s\n", fIdx, diag
$getFaultNodeId(fIdx), diag$getFaultOutcomeId(fIdx)))}}
printFaultInfo <- function(net, fault) {cat(sprintf("%-20s %-20s %-24s %-8s\n",SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

165

Tutorials
net$getNodeId(fault$node), net$getOutcomeId(fault$node, fault$outcome),
fault$probability, ifelse(fault$isPursued, "Yes", "No")))}printObservationInfo <- function(net, observation) {cat(sprintf("%-20s %-24s %-8s %-20s\n", net$getNodeId(observation$node),
observation$measure, observation$cost, observation$infoGain))}
printDiagResults <- function(net, diagResults) {cat("\n-- Diag results start\n\nFaults:\n")cat(sprintf("%-20s %-20s %-24s %-8s\n","Node Id", "Outcome Id", "Probability", "Is Pursued"))for (faultInfo in diagResults$faults) {printFaultInfo(net, faultInfo)}cat("\nObservations:\n")cat(sprintf("%-20s %-24s %-8s %-20s\n","Node Id", "Measure", "Cost", "InfoGain"))for (observationInfo in diagResults$observations) {printObservationInfo(net, observationInfo)}cat("\nDiag results end\n")}cat("Starting Tutorial9\n")net <- Network()
net$readFile("HeparII.xdsl")
cat("Hepar model loaded\n")printDiagTypes(net)cat("Creating diagnostic network object\n")diag <- DiagNetwork(net)
printFaultIndices(diag)pursuedFaultIdx <- diag$getPursuedFault()cat(sprintf("The default (most likely) pursued fault is at index %d: %s=%s\n",pursuedFaultIdx,
diag$getFaultNodeId(pursuedFaultIdx), diag$getFaultOutcomeId(pursuedFaultIdx)))cat("Running diagnosis with no instantiated observations\n")diagResults <- diag$update()
printDiagResults(net, diagResults)cat("Running diagnosis with three observations\n")diag$instantiateObservation("jaundice", "present")
diag$instantiateObservation("nausea", "absent")
diag$instantiateObservation("obesity", "present")
diagResults <- diag$update()
printDiagResults(net, diagResults)cat("Running diagnosis with two observations and focusing on Hyperbilirubinemia\n")diag$releaseObservation("nausea")
diag$instantiateObservation("obesity", "absent")
hyperbilirubinemiaIdx <- diag$getFaultIndex("Hyperbilirubinemia", "present")

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

166

Tutorials
diag$setPursuedFault(hyperbilirubinemiaIdx)
diagResults <- diag$update()
printDiagResults(net, diagResults)cat("Switching algorithm to cross-entropy, observations and pursued fault unchanged")
diag$setSingleFaultAlgorithm(diag$SingleFaultAlgorithmType$CROSSENTROPY)
diagResults <- diag$update()
printDiagResults(net, diagResults)cat("Running diagnosis with two observations","and focusing on both Hyperbilirubinemia and Steatosis\n")steatosisIdx <- diag$getFaultIndex("Steatosis", "present")
diag$setPursuedFaults(c(steatosisIdx, hyperbilirubinemiaIdx))
diagResults <- diag$update()
printDiagResults(net, diagResults)cat("Swtiching algorithm to L2 distance, observations and pursued faults unchanged")
diag$setMultiFaultAlgorithm(diag$MultiFaultAlgorithmType$L2_NORMALIZED_DISTANCE)
diagResults <- diag$update()
printDiagResults(net, diagResults)cat("Tutorial9 complete.\n")7.9.4

Tutorial9.cs

using System;
using System.Collections.Generic;using Smile;// Tutorial9 loads HeparII.xdsl file// and runs multiple diagnostic algorithms// Use the link below to download the HeparII.xdsl file:// https://support.bayesfusion.com/docs/Examples/Discrete%20Bayesian%20Networks/HeparII.xdsl
namespace SmileNetTutorial{class Tutorial9{
public static void Run(){
Console.WriteLine(System.IO.Directory.GetCurrentDirectory());
Console.WriteLine("Starting Tutorial9...");
Network net = new Network();
net.ReadFile("HeparII.xdsl");Console.WriteLine("Hepar model loaded");PrintDiagTypes(net);
Console.WriteLine("Creating diagnostic network object");
DiagNetwork diag = new DiagNetwork(net);
PrintFaultIndices(diag);
int pursuedFaultIdx = diag.GetPursuedFault();
Console.WriteLine("The default (most likely) pursued fault is at index {0}: {1}={2}",pursuedFaultIdx,
diag.GetFaultNodeId(pursuedFaultIdx), diag.GetFaultOutcomeId(pursuedFaultIdx));

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

167

Tutorials
Console.WriteLine("Running diagnosis with no instantiated observations");
DiagResults diagResults = diag.Update();PrintDiagResults(net, diagResults);
Console.WriteLine("Running diagnosis with three observations");
diag.InstantiateObservation("jaundice", "present");
diag.InstantiateObservation("nausea", "absent");
diag.InstantiateObservation("obesity", "present");
diagResults = diag.Update();PrintDiagResults(net, diagResults);

Console.WriteLine("Running diagnosis with two observations and "+ "focusing on Hyperbilirubinemia");diag.ReleaseObservation("nausea");diag.InstantiateObservation("obesity", "absent");
int hyperbilirubinemiaIdx = diag.GetFaultIndex("Hyperbilirubinemia", "present");
diag.SetPursuedFault(hyperbilirubinemiaIdx);
diagResults = diag.Update();PrintDiagResults(net, diagResults);
Console.WriteLine("Switching algorithm to cross-entropy, "+ "observations and pursued fault unchanged");diag.SingleFaultAlgorithm = DiagNetwork.SingleFaultAlgorithmType.Crossentropy;
diagResults = diag.Update();PrintDiagResults(net, diagResults);
Console.WriteLine("Running diagnosis with two observations and "+ "focusing on both Hyperbilirubinemia and Steatosis");int steatosisIdx = diag.GetFaultIndex("Steatosis", "present");
diag.SetPursuedFaults(new int[] { steatosisIdx, hyperbilirubinemiaIdx });diagResults = diag.Update();PrintDiagResults(net, diagResults);
Console.WriteLine("Swtiching algorithm to L2 distance, "+ "observations and pursued faults unchanged");diag.MultiFaultAlgorithm = DiagNetwork.MultiFaultAlgorithmType.L2NormalizedDistance;
diagResults = diag.Update();PrintDiagResults(net, diagResults);
Console.WriteLine("Tutorial9 complete.");}public static void PrintDiagTypes(Network net){
Console.WriteLine("Network has {0} nodes", net.GetNodeCount());
Console.WriteLine("{0,-20} {1,-30} {2,-30}","Node Id", "Diagnostic Type", "Fault Outcomes");string[] nodeIds = net.GetAllNodeIds();
foreach (string nodeId in nodeIds){
Network.NodeDiagType diagType = net.GetNodeDiagType(nodeId);
List<string> faultOutcomes = new List<string>();if (diagType == Network.NodeDiagType.Fault){

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

168

Tutorials
foreach (string outcomeId in net.GetOutcomeIds(nodeId)){if (net.IsFaultOutcome(nodeId, outcomeId)){
faultOutcomes.Add(outcomeId);}}}
Console.WriteLine("{0,-20} {1,-30} {2,-30}", nodeId, diagType,(faultOutcomes.Count != 0) ? string.Join(", ", faultOutcomes) : "n/a");}}public static void PrintFaultIndices(DiagNetwork diag){
int faultCount = diag.FaultCount;
Console.WriteLine("Diagnostic network has {0} faults (node/outcome pairs)",
faultCount);
Console.WriteLine("{0,-12} {1,-20} {2,-20}","Fault Index", "Fault Node Id", "Fault Outcome Id");for (int fIdx = 0; fIdx < faultCount; fIdx++){
Console.WriteLine("{0,-12} {1,-20} {2,-20}", fIdx,
diag.GetFaultNodeId(fIdx), diag.GetFaultOutcomeId(fIdx));}}
public static void PrintFaultInfo(Network net, FaultInfo fault){
Console.WriteLine("{0,-20} {1,-20} {2,-24} {3,-8}",
net.GetNodeId(fault.Node), net.GetOutcomeId(fault.Node, fault.Outcome),
fault.Probability, fault.IsPursued ? "Yes" : "No");}public static void PrintObservationInfo(Network net, ObservationInfo observation){
Console.WriteLine("{0,-20} {1,-24} {2,-8} {3,-20}",
net.GetNodeId(observation.Node), observation.Measure,observation.Cost, observation.InfoGain);}public static void PrintDiagResults(Network net, DiagResults diagResults){
Console.WriteLine("Diag results start\n\nFaults:");Console.WriteLine("{0,-20} {1,-20} {2,-24} {3,-8}","Node Id", "Outcome Id", "Probability", "Is Pursued");foreach (FaultInfo faultInfo in diagResults.Faults){
PrintFaultInfo(net, faultInfo);}
Console.WriteLine("\nObservations:");Console.WriteLine("{0,-20} {1,-24} {2,-8} {3,-20}","Node Id", "Measure", "Cost", "InfoGain");foreach (ObservationInfo observationInfo in diagResults.Observations){

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

169

Tutorials
PrintObservationInfo(net, observationInfo);}
Console.WriteLine("\nDiag results end\n");}}}7.10 Tutorial 10: Structure learning
Suppose we have a file Credit10K.csv consisting of 10,000 records of customers collected at a bank. Each ofthese customers was measured on several variables, Payment History, Work History, Reliability, Debit,
Income, Ratio of Debts to Income, Assets, Worth, Profession, Future Income, Age and Credit Worthiness. The
file is too large to include in this manual, but it can be downloaded fromhttps://support.bayesfusion.com/docs/Examples/Learning/Credit10K.csv. It is also included in the
Examples\Learning directory of GeNIe installation. The first few records of the file (included among theexample files with GeNIe distribution) look as follows in GeNIe:

We want to learn the structure of the Bayesian network based on the data. Our program attempts to load thedata file from disk, and prints the number of data set variables and records if loading was successful. For clarity,
we omit the exception handling in the code snippets here. The tutorial programs include the exception handlers
for each learning call.
Java:
DataSet ds = new DataSet();ds.readFile("Credit10k.csv");
System.out.println(String.format("Dataset has %d variables (columns) and %d records (rows)",
ds.getVariableCount(), ds.getRecordCount()));
Python:ds = pysmile.learning.DataSet()
ds.read_file("Credit10k.csv")
print(f"Dataset has {ds.get_variable_count()} variables (columns)
and {ds.get_record_count()} records (rows)")C#:
DataSet ds = new DataSet();ds.ReadFile("Credit10k.csv");
Console.WriteLine(String.Format("Dataset has {0} variables (columns) and {1} records (rows)",

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

170

Tutorials
ds.VariableCount, ds.RecordCount));
The most general structure learning algorithm is Bayesian Search. It is a hill climbing procedure with random
restarts, guided by log-likelihood scoring heuristic. Its search space is hyper-exponential, so for best results thealgorithm can be fine-tuned with multiple with multiple options (details are available in the SMILE manual's
Reference section). We set the number of iterations (random restarts) to 50, and fix the random number seed toensure reproducible results. The learn method requires an input data set. The learning algorithm will perform
50 iterations, each starting with randomized structure, and compute a log-likelihood score for at the end of each
iteration. The best structure will be copied into the output network, and parameter learning will be runinternally. The output network will therefore be ready to use.
Java:
BayesianSearch bayesSearch = new BayesianSearch();bayesSearch.setIterationCount(50);bayesSearch.setRandSeed(9876543);
Network net1 = bayesSearch.learn(ds);Python:bayes_search = pysmile.learning.BayesianSearch()bayes_search.set_iteration_count(50)bayes_search.set_rand_seed(9876543)
net1 = bayes_search.learn(ds)C#:BayesianSearch bayesSearch = new BayesianSearch();bayesSearch.IterationCount = 50;
bayesSearch.RandSeed = 9876543;
Network net1 = bayesSearch.Learn(ds);The output network looks like this in GeNIe:

Please note that the nodes in the network learned in SMILE occupy the same screen position. The image below
shows the network after the parent ordering layout has been applied. We plan to include simple layout as the last
step of the learning in the upcoming release of SMILE.
The network is saved for reference in the tutorial9-bs1.xdsl file. We can obtain a different structure by changing
the seed for the random number generator and calling learn again. The selected seed value is 3456789, and theoutput looks like this:

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

171

Tutorials

The tutorial saves this network to tutorial9-bs2.xdsl proceeds to third attempt at Bayesian Search, this timewith background knowledge. We want to forbid the arc between Age and CreditWorthiness, and force an arc
between Age and Profession. When specifying the background knowledge, we refer to the data set variables as
their indices, so we need to convert the textual identifiers to indices first.
Java:
int idxAge = ds.findVariable("Age");
int idxProfession = ds.findVariable("Profession");int idxCreditWorthiness = ds.findVariable("CreditWorthiness");
BkKnowledge backgroundKnowledge = new BkKnowledge();backgroundKnowledge.matchData(ds);backgroundKnowledge.addForbiddenArc(idxAge, idxCreditWorthiness);backgroundKnowledge.addForcedArc(idxAge, idxProfession);bayesSearch.setBkKnowledge(backgroundKnowledge);
Network net3 = bayesSearch.Learn(ds);Python:idx_age = ds.find_variable("Age")
idx_profession = ds.find_variable("Profession")
idx_credit_worthiness = ds.find_variable("CreditWorthiness")background_knowledge = pysmile.learning.BkKnowledge()
background_knowledge.match_data(ds)background_knowledge.add_forbidden_arc(idx_age, idx_credit_worthiness)background_knowledge.add_forced_arc(idx_age, idx_profession)
bayes_search.set_bk_knowledge(background_knowledge)
net3 = bayes_search.learn(ds)C#:
int idxAge = ds.FindVariable("Age");
int idxProfession = ds.FindVariable("Profession");int idxCreditWorthiness = ds.FindVariable("CreditWorthiness");
BkKnowledge backgroundKnowledge = new BkKnowledge();backgroundKnowledge.MatchData(ds);backgroundKnowledge.AddForbiddenArc(idxAge, idxCreditWorthiness);backgroundKnowledge.AddForcedArc(idxAge, idxProfession);bayesSearch.BkKnowledge = backgroundKnowledge;

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

172

Tutorials
Network net3 = bayesSearch.Learn(ds);The output network now looks as follows - please note the absence of an arc from Age to CreditWorthiness andthe forced arc between Age and Profession. This network is saved to tutorial9-bs3.xdsl.

We now change our learning algorithm to Tree Augmented Naive Bayes (TAN). The TAN algorithm starts with a
Naive Bayes structure (i.e., one in which the class variable is the only parent of all remaining, feature variables)
and adds connections between the feature variables to account for possible dependence between them,conditional on the class variable. The algorithm imposes the limit of only one additional parent of every feature
variable (additional to the class variable, which is a parent of every feature variable). We need to specify the classvariable with its textual identifier. In this example CreditWorthiness is the class variable.
Java:
Network net4;
TAN tan = new TAN();tan.setRandSeed(777999);
tan.setClassVariableId("CreditWorthiness");
net4 = tan.learn(ds);Python:tan = pysmile.learning.TAN()
tan.set_rand_seed(777999)
tan.set_class_variable_id("CreditWorthiness")
net4 = tan.learn(ds)C#:Network net4;
TAN tan = new TAN();tan.RandSeed = 777999;
tan.ClassVariableId = "CreditWorthiness";
net4 = tan.Learn(ds);The program saves the network for reference in tutorial9-tan.xdsl file. The network looks as follows (the classnode has yellow background):

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

173

Tutorials

Our final structure learning algorithm will be PC, which uses independences observed in data (established bymeans of classical independence tests) to infer the structure that has generated them. The output of the PC
algorithm is a pattern, which is an adjacency matrix, and does not necessarily represent a directed acyclic graph(DAG). The pattern can be converted to network with the makeNetwork method, which enforces the DAG
criterion on the pattern, and copies the variables and edges to the network.
Java:
PC pc = new PC();
Pattern pattern = pc.learn(ds);
Network net5 = pattern.makeNetwork(ds);Python:pc = pysmile.learning.PC()
pattern = pc.learn(ds)net5 = pattern.make_network(ds)C#:PC pc = new PC();
Pattern pattern = pc.Learn(ds);
Network net5 = pattern.MakeNetwork(ds);
At this point our output network has nodes with outcomes based on the labels in the data set, but the probability
distributions in node definitions are uniform over outcomes. To complete the learning process, we need tomanually call a parameter learning procedure implemented in the EM class.
Java:
EM em = new EM();
DataMatch[] matching = ds.matchNetwork(net5);em.setUniformizeParameters(false);
em.setRandomizeParameters(false);
em.setEqSampleSize(0);em.learn(ds, net5, matching);
Python:em = pysmile.learning.EM()
matching = ds.match_network(net5)
em.set_uniformize_parameters(False)

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

174

Tutorials
em.set_randomize_parameters(False)
em.set_eq_sample_size(0)em.learn(ds, net5, matching)C#:
EM em = new EM();
DataMatch[] matching = ds.MatchNetwork(net5);
em.UniformizeParameters = false;
em.RandomizeParameters = false;
em.EqSampleSize = 0;em.Learn(ds, net5, matching);
Note the matchNetwork call, which populates the matching variable using identifiers of data set variables andnetwork nodes. This information is subsequently used by EM.learn to associate data set variables with network
node. The output network looks like as follows:

The network is saved for reference in the tutorial9-pc-em.xdsl file and Tutorial 9 finishes.

7.10.1 Tutorial10.java
package tutorials;
import smile.*;import smile.learning.*;// Tutorial9 loads Credit10k.csv file// and runs multiple structure learning algorithms// using the loaded dataset.
// Use the link below to download the Credit10k.csv file:// https://support.bayesfusion.com/docs/Examples/Learning/Credit10K.csv
public class Tutorial10 {public static void run() {System.out.println("Starting Tutorial9...");

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

175

Tutorials
DataSet ds = new DataSet();try{
ds.readFile("Credit10k.csv");} catch (SMILEException ex){
System.out.println("Dataset load failed");return;}System.out.printf("Dataset has %d variables (columns) and %d records (rows)\n",ds.getVariableCount(), ds.getRecordCount());
BayesianSearch bayesSearch = new BayesianSearch();bayesSearch.setIterationCount(50);bayesSearch.setRandSeed(9876543);
Network net1;
try{
net1 = bayesSearch.learn(ds);} catch (SMILEException ex){
System.out.println("Bayesian Search failed");return;}System.out.printf("1st Bayesian Search finished, structure score: %f\n",bayesSearch.getLastScore());
net1.writeFile("tutorial9-bs1.xdsl");
Network net2;
bayesSearch.setRandSeed(3456789);
try{
net2 = bayesSearch.learn(ds);}catch (SMILEException ex){
System.out.println("Bayesian Search failed");return;}System.out.printf("2nd Bayesian Search finished, structure score: %f\n",bayesSearch.getLastScore());
net2.writeFile("tutorial9-bs2.xdsl");
int idxAge = ds.findVariable("Age");
int idxProfession = ds.findVariable("Profession");int idxCreditWorthiness = ds.findVariable("CreditWorthiness");
if (idxAge < 0 || idxProfession < 0 || idxCreditWorthiness < 0){
System.out.println("Can't find dataset variables for background knowledge");
System.out.println("The loaded file may not be Credit10k.csv");
return;}BkKnowledge backgroundKnowledge = new BkKnowledge();backgroundKnowledge.matchData(ds);backgroundKnowledge.addForbiddenArc(idxAge, idxCreditWorthiness);

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

176

Tutorials
backgroundKnowledge.addForcedArc(idxAge, idxProfession);bayesSearch.setBkKnowledge(backgroundKnowledge);
Network net3;
try{
net3 = bayesSearch.learn(ds);}catch (SMILEException ex){
System.out.println("Bayesian Search failed");return;}System.out.printf("3rd Bayesian Search finished, structure score: %f\n",bayesSearch.getLastScore());
net3.writeFile("tutorial9-bs3.xdsl");
Network net4;
TAN tan = new TAN();tan.setRandSeed(777999);
tan.setClassVariableId("CreditWorthiness");try{
net4 = tan.learn(ds);}catch (SMILEException ex){
System.out.println("TAN failed");return;}System.out.println("Tree-augmented Naive Bayes finished");net4.writeFile("tutorial9-tan.xdsl");
PC pc = new PC();
Pattern pattern;try{pattern = pc.learn(ds);} catch (SMILEException ex){
System.out.println("PC failed");return;}Network net5 = pattern.makeNetwork(ds);System.out.println("PC finished, proceeding to parameter learning");net5.writeFile("tutorial9-pc.xdsl");
EM em = new EM();
DataMatch[] matching;try{
matching = ds.matchNetwork(net5);}catch (SMILEException ex){
System.out.println("Can't automatically match network with dataset");return;SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

Tutorials

177}
em.setUniformizeParameters(false);
em.setRandomizeParameters(false);
em.setEqSampleSize(0);try{em.learn(ds, net5, matching);}
catch (SMILEException ex){
System.out.println("EM failed");return;}System.out.println("EM finished");net5.writeFile("tutorial9-pc-em.xdsl");
System.out.println("Tutorial10 complete");}}7.10.2 Tutorial10.py
import pysmile# Tutorial10 loads Credit10k.csv file# and runs multiple structure learning algorithms# using the loaded dataset.
# Use the link below to download the Credit10k.csv file:# https://support.bayesfusion.com/docs/Examples/Learning/Credit10K.csv
class Tutorial10:
def __init__(self):
print("Starting tutorial10...")
ds = pysmile.learning.DataSet()try:
ds.read_file("Credit10k.csv")
except pysmile.SMILEException:
print("Dataset load failed")
returnprint(f"Dataset has {ds.get_variable_count()} variables (columns) "+ f"and {ds.get_record_count()} records (rows)")
bayes_search = pysmile.learning.BayesianSearch()bayes_search.set_iteration_count(50)bayes_search.set_rand_seed(9876543)
try:
net1 = bayes_search.learn(ds)except pysmile.SMILEException:
print("Bayesian Search failed")
returnprint(f"1st Bayesian Search finished, structure score: {bayes_search.get_last_score()}")
net1.write_file("tutorial10-bs1.xdsl")
bayes_search.set_rand_seed(3456789)
try:
net2 = bayes_search.learn(ds)SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

178

Tutorials

except pysmile.SMILEException:
print("Bayesian Search failed")
returnprint(f"2nd Bayesian Search finished, structure score: {bayes_search.get_last_score()}")
net2.write_file("tutorial10-bs2.xdsl")
idx_age = ds.find_variable("Age")
idx_profession = ds.find_variable("Profession")
idx_credit_worthiness = ds.find_variable("CreditWorthiness")
if idx_age < 0 or idx_profession < 0 or idx_credit_worthiness < 0:
print("Can't find dataset variables for background knowledge")
print("The loaded file may not be Credit10k.csv")
returnbackground_knowledge = pysmile.learning.BkKnowledge()
background_knowledge.match_data(ds)background_knowledge.add_forbidden_arc(idx_age, idx_credit_worthiness)background_knowledge.add_forced_arc(idx_age, idx_profession)
bayes_search.set_bk_knowledge(background_knowledge)try:
net3 = bayes_search.learn(ds)except pysmile.SMILEException:
print("Bayesian Search failed")
returnprint(f"3rd Bayesian Search finished, structure score: {bayes_search.get_last_score()}")
net3.write_file("tutorial10-bs3.xdsl")
tan = pysmile.learning.TAN()
tan.set_rand_seed(777999)
tan.set_class_variable_id("CreditWorthiness")try:
net4 = tan.learn(ds)except pysmile.SMILEException:
print("TAN failed")
returnprint("Tree-augmented Naive Bayes finished")net4.write_file("tutorial10-tan.xdsl")
pc = pysmile.learning.PC()
try:pattern = pc.learn(ds)except pysmile.SMILEException:
print("PC failed")
returnnet5 = pattern.make_network(ds)print("PC finished, proceeding to parameter learning")
net5.write_file("tutorial10-pc.xdsl")
em = pysmile.learning.EM()
try:
matching = ds.match_network(net5)
except pysmile.SMILEException:
print("Can't automatically match network with dataset")
returnem.set_uniformize_parameters(False)

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

Tutorials

179

em.set_randomize_parameters(False)
em.set_eq_sample_size(0)try:em.learn(ds, net5, matching)
except pysmile.SMILEException:
print("EM failed")
returnprint("EM finished")net5.write_file("tutorial10-pc-em.xdsl")
print("Tutorial10 complete.")7.10.3 Tutorial10.R
library(rSMILE)
source("License.R")# Tutorial10 loads Credit10k.csv file# and runs multiple structure learning algorithms# using the loaded dataset.
# Use the link below to download the Credit10k.csv file:# https://support.bayesfusion.com/docs/Examples/Learning/Credit10K.csv
cat("Starting Tutorial10...\n")ds <- DataSet()
ds$readFile("Credit10k.csv")
cat(sprintf("Dataset has %d variables (columns) and %d records (rows)\n",
ds$getVariableCount(), ds$getRecordCount()))
bayesSearch <- BayesianSearch()bayesSearch$setIterationCount(50)bayesSearch$setRandSeed(9876543)
net1 <- bayesSearch$learn(ds)cat(sprintf("1st Bayesian Search finished, structure score: %f\n",bayesSearch$getLastScore()))
net1$writeFile("tutorial10-bs1.xdsl")
bayesSearch$setRandSeed(3456789)
net2 <- bayesSearch$learn(ds)cat(sprintf("2nd Bayesian Search finished, structure score: %f\n",bayesSearch$getLastScore()))
net2$writeFile("tutorial10-bs2.xdsl")
idxAge <- ds$findVariable("Age")
idxProfession <- ds$findVariable("Profession")
idxCreditWorthiness <- ds$findVariable("CreditWorthiness")backgroundKnowledge <- BkKnowledge()
backgroundKnowledge$matchData(ds)backgroundKnowledge$addForbiddenArc(idxAge, idxCreditWorthiness)backgroundKnowledge$addForcedArc(idxAge, idxProfession)
bayesSearch$setBkKnowledge(backgroundKnowledge)
net3 <- bayesSearch$learn(ds)cat(sprintf("3rd Bayesian Search finished, structure score: %f\n",SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

Tutorials

180

bayesSearch$getLastScore()))
net2$writeFile("tutorial10-bs3.xdsl")
tan <- TAN()
tan$setRandSeed(777999)
tan$setClassVariableId("CreditWorthiness")
net4 <- tan$learn(ds)cat("Tree-augmented Naive Bayes finished")
net4$writeFile("tutorial10-tan.xdsl")
pc <- PC()
pattern <- pc$learn(ds)net5 <- pattern$makeNetwork(ds)cat("PC finished, proceeding to parameter learning\n")
net5$writeFile("tutorial10-pc.xdsl")
em <- EM()
matching <- ds$matchNetwork(net5)
em$setUniformizeParameters(FALSE)
em$setRandomizeParameters(FALSE)
em$setEqSampleSize(0)em$learn(ds, net5, matching)cat("EM finished\n")
net5$writeFile("tutorial10-pc-em.xdsl")
cat("Tutorial10 complete.\n")7.10.4 Tutorial10.cs
using System;
using Smile;using Smile.Learning;// Tutorial10 loads Credit10k.csv file// and runs multiple structure learning algorithms// using the loaded dataset.
// Use the link below to download the Credit10k.csv file:// https://support.bayesfusion.com/docs/Examples/Learning/Credit10K.csv
namespace SmileNetTutorial{class Tutorial10{
public static void Run(){
Console.WriteLine("Starting Tutorial10...");
DataSet ds = new DataSet();try{
ds.ReadFile("Credit10k.csv");} catch (SmileException){
Console.WriteLine("Dataset load failed");return;}Console.WriteLine("Dataset has {0} variables (columns) and {1} records (rows)",

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

181

Tutorials
ds.VariableCount, ds.RecordCount);
BayesianSearch bayesSearch = new BayesianSearch();bayesSearch.IterationCount = 50;
bayesSearch.RandSeed = 9876543;
Network net1;
try{
net1 = bayesSearch.Learn(ds);} catch (SmileException){
Console.WriteLine("Bayesian Search failed");return;}Console.WriteLine("1st Bayesian Search finished, structure score: {0}",bayesSearch.LastScore);
net1.WriteFile("tutorial10-bs1.xdsl");
Network net2;
bayesSearch.RandSeed = 3456789;
try{
net2 = bayesSearch.Learn(ds);}catch (SmileException){
Console.WriteLine("Bayesian Search failed");return;}Console.WriteLine("2nd Bayesian Search finished, structure score: {0}",bayesSearch.LastScore);
net2.WriteFile("tutorial10-bs2.xdsl");

int idxAge = ds.FindVariable("Age");
int idxProfession = ds.FindVariable("Profession");int idxCreditWorthiness = ds.FindVariable("CreditWorthiness");
if (idxAge < 0 || idxProfession < 0 || idxCreditWorthiness < 0){
Console.WriteLine("Can't find dataset variables for background knowledge");
Console.WriteLine("The loaded file may not be Credit10k.csv");
return;}BkKnowledge backgroundKnowledge = new BkKnowledge();backgroundKnowledge.MatchData(ds);backgroundKnowledge.AddForbiddenArc(idxAge, idxCreditWorthiness);backgroundKnowledge.AddForcedArc(idxAge, idxProfession);bayesSearch.BkKnowledge = backgroundKnowledge;
Network net3;
try{
net3 = bayesSearch.Learn(ds);}catch (SmileException){
Console.WriteLine("Bayesian Search failed");SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

182

Tutorials
return;}Console.WriteLine("3rd Bayesian Search finished, structure score: {0}",bayesSearch.LastScore);
net3.WriteFile("tutorial10-bs3.xdsl");
Network net4;
TAN tan = new TAN();tan.RandSeed = 777999;
tan.ClassVariableId = "CreditWorthiness";try{
net4 = tan.Learn(ds);}catch (SmileException){
Console.WriteLine("TAN failed");return;}Console.WriteLine("Tree-augmented Naive Bayes finished");net4.WriteFile("tutorial10-tan.xdsl");
PC pc = new PC();
Pattern pattern;try{pattern = pc.Learn(ds);} catch (SmileException){
Console.WriteLine("PC failed");return;}Network net5 = pattern.MakeNetwork(ds);Console.WriteLine("PC finished, proceeding to parameter learning");net5.WriteFile("tutorial10-pc.xdsl");
EM em = new EM();
DataMatch[] matching;try{
matching = ds.MatchNetwork(net5);}catch (SmileException){
Console.WriteLine("Can't automatically match network with dataset");return;}em.UniformizeParameters = false;
em.RandomizeParameters = false;
em.EqSampleSize = 0;
try{
em.Learn(ds, net5, matching);}
catch (SmileException){

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

183

Tutorials
Console.WriteLine("EM failed");return;}Console.WriteLine("EM finished");net5.WriteFile("tutorial10-pc-em.xdsl");Console.WriteLine("Tutorial10 complete");}}}SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

This page is intentionally left blank.

Acknowledgments

186

Acknowledgments
8

Acknowledgments

SMILE internally uses portions of the following two software libraries: micro-ECC and Expat. PySMILE uses
pybind11. These three libraries require an acknowledgment that we are reproducing below.

micro-ECC
Copyright (c) 2014, Kenneth MacKay
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided thatthe following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following
disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and thefollowing disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIESOF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENTSHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITEDTO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANYWAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Expat
Copyright (c) 1998-2000 Thai Open Source Software Center Ltd and Clark Cooper
Copyright (c) 2001-2017 Expat maintainers
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associateddocumentation files (the "Software"), to deal in the Software without restriction, including without limitation therights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and topermit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of
the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

pybindSMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024

187

Acknowledgments
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>, All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided thatthe following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and thefollowing disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIESOF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENTSHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITEDTO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVE CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANYWAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

SMILE Wrappers

Version 2.2.4.R1, Built on 4/27/2024