The Analytica Decision Engine Tutorial

ADE User Guide > The Analytica Decision Engine Tutorial

This tutorial shows you how to use the Analytica Decision Engine (ADE) from within a Visual Basic program.

Your first ADE application

First let’s write a simple ADE application from scratch, just to be sure that everything is set up correctly. Follow these steps:

  1. Bring up Visual Studio.NET.
  2. Select New Project, then select the Project Type “Visual Basic Projects” and the template “Console Application.” Select a project name, e.g., “FirstADEtry,” and an appropriate folder location.
  3. From the Project menu, select Add Reference and select the COM tab in the dialog. Find and select Analytica Decision Engine Local Server 4.6 (Ade.exe) and click OK.
    • Note: If you have installed ADE 64-bit and do not have ADE 32-bit installed, Visual Studio may not show Analytica Decision Engine Local Server 4.6, even though it is properly installed. This occurs because Visual Studio itself is a 32-bit application. If this happens, select the Browse tab on the Add Reference dialog, navigate to the ADE install directory (usually C:\Program Files\Lumina\ADE 4.6), and select ADE64.exe.
    • Note: If you cannot find this entry in the list of COM servers, then ADE 4.6 is not properly installed. See Installation of ADE for instruction on how to install ADE before reading further.
  4. Add to the Module1 class as follows:
    Imports ADE
    Module Module1
    Public ADE As CAEngine
    Sub Main()
    Dim FileName, ModelName As String
    FileName = "C:\Program Files\Lumina\Analytica 4.6"
    FileName &= "\Example Models\Tutorial Models\Car Cost.ana"
    ADE = New CAEngine
    ADE.MonitorProcess( System.Diagnostics.Process.GetCurrentProcess().Id )
    ModelName = ADE.OpenModel(FileName)
    If ModelName = "" Then
    Console.Write(FileName & " not found")
    Else
    Console.Write("Congratulations on opening ")
    Console.WriteLine(ModelName)
    Console.WriteLine("Press 'enter' to exit")
    Console.ReadLine()
    End If
    End Sub
    End Module
  5. Now just run the program. If your program prints “Congratulations on opening Carcosts” you have just successfully written your first ADE program.


This first program did the following:

  • Created a CAEngine automation object called ADE (using new CAEngine).
  • Opened an Analytica model (using the OpenModel method of CAEngine).
  • Displayed the name of the model (the return value of OpenModel).

We go into the details of these and other functions in the next section.

What's next?

We will not attempt to explain all of the features of ADE in this tutorial. These are described in the following chapters of this guide. Here, we give you the background to explore the more advanced features of ADE on your own.

From this point, we use the example model called Txc.ana. You can find Txc.ana in the Risk Analysis folder under the Example Models folder installed with Analytica. If you cannot find it, or if you opted not to install the examples when you originally installed Analytica, there is a copy in the Examples\Tutorial.NET folder in the directory where you installed ADE.

The Txc model demonstrates risk-benefit analysis of reducing the emissions of the fictitious air pollutant TXC. Please open the Txc model with Analytica to see how it works.

The example Visual Basic.NET program called TestTxc in your ADE Examples\Tutorial.NET folder shows many aspects of ADE. This program creates an ADE automation object, opens the Txc.ana model with this object, gets the definition of the Population Exposed variable, evaluates the Total Cost variable, prints out the result of the Total Cost variable as a table by getting at the individual components of the table, and changes the definition of the Population Exposed variable. It then gets the result of the Total Cost variable again, to see what effect the change of definition for Population Exposed had on the Total Cost variable. If things are set up properly, TestTxc displays the window shown in “Text Txc window”.

The application displays the definition of the Population Exposed variable ("Normal (30M, 3M)"), and the table associated with Total Cost, based on the definition of Population Exposed. You can change the definition of Population Exposed by selecting File > Change Population Exposed from the main menu and seeing the effect this has on the Total Cost table.

TextTxcWindow.png

Distinguishing title from identifier

Whenever an ADE function requires a variable, you must pass it the identifier of the variable, not its title. This can be confusing since Analytica normally displays the titles of each variable in an influence diagram. By default, when you first create each object, Analytica automatically creates an identifier based on the title. It substitutes an underscore (_) for each blank or other character in the title that is not a letter or number.

You can show the identifiers in an influence diagram by pressing Control+y (or by selecting Show by identifier from the Object menu). For model Txc.ana, you can see that the identifier of the variable titled Population Exposed is Pop_exp. It is important to use Pop_exp as the identifier when passing this variable to ADE functions. ADE would not be able to find the variable if you pass Population Exposed, and would return an error.

Creating an ADE object from within Visual Basic

If you haven’t already, load the project called Examples\Tutorial\TestTxc.sln into Visual Basic.NET, and view the code for the file called TestTxc.vb. The code looks like this:

Imports ADEW
. . .
Public adeEngine As CAEngine
Public Sub Main()
Dim exeDirectory, theModel As String
Dim theModelString As String
exeDirectory = VB6.GetPath
theModel = exeDirectory & "\..\" & "Txc.ana"
adeEngine = New CAEngine
...
theModelString = adeEngine.OpenModel(theModel)
...
frmMain.DefInstance.Show()
End Sub

At the very top of the file, the code declares the automation object adeEngine as a CAEngine object. Using this object, we can access all of the public functions exposed by CAEngine (see “ADE Server Class Reference” on page 51 for a complete listing). This line then creates the CAEngine object.

adeEngine = New CAEngine

The adeEngine variable now holds our in-process CAEngine object. If we want to use the local (out-of-process) server version of ADE, we can add a reference to the project to the Analytica Decision Engine Server 4.6 COM component and change the top line from Imports ADEW to Imports ADE.

Here is another way to obtain a new CAEngine object. This sequence does not require adding a reference to the project.

adeEngine = CreateObject("ADEW4.6.CAEngine") ’ in-process
adeEngine = CreateObject("ADE4.6.CAEngine") ’ out-of-process

To understand the pros and cons of using an in-process server versus as out-of-process (or local) server, and which automation server to use for different scenarios, see “In-process vs. out-of-process” on page 22, as well as other books related to COM servers.

COM vs. Automation interface

In the example above, we used a COM interface to call ADE. In a COM interface, the object (CAEngine in this case) is declared as CAEngine, and the compiler resolves each member function and can detect several obvious errors at compile time. In addition, Visual Studio can provide a list of methods and parameter types as tool tips as you program, which is helpful when writing programs that use ADE. COM calls are slightly faster than Automation calls, but the speed difference is not usually significant in applications of ADE. With ADE 4.6, we recommend using the COM interface if your programming language supports it.

In VB Automation, you can declare an object simply as Object, rather than a more specific types such as CAEngine, CAObject, and so on. When ADE methods are called using Automation, the methods are resolved at run time. At compile time, the compiler does not know whether your ADE object has a function named OpenModel. In VB, the syntax for calling a COM method or an Automation method is identical — the only difference is whether the object’s type is declared explicitly.

In VC++ and C#, the syntax for calling COM is not the same as for Automation. In these cases, COM is much more convenient, while Automation can get rather tedious. However, some languages, including VBScript and other scripting languages, support only Automation and not COM.

Monitoring the Process

When using the out-of-process ADE server, your own code must release the CAEngine COM object when it terminates. When this final CAEngine usage is released, the ADE.exe process automatically terminates. In the code seen so far, the VB language takes care releasing the object automatically when it reaches the end of the program. However, while you are debugging your own code, you may terminate your program prematurely to fix a bug, your program may be killed from Task Manager, or your own code may crash, causing your program’s process to terminate before it had a chance to release the object. Because the COM object is never released, the ADE.exe process cannot know that it is no longer in use, and you may get zombie ADE.exe processes lingering.

To avoid this, it is a good practice to call CAEngine::MonitorProcess(..) immediately after obtaining a CAEngine instance. You pass the method the process id for your program’s own process. In this fashion, ADE can learn which process is using it, and will set up a thread to detect if your process terminates before ADE is fully released. If your program’s process does terminate, the ADE process immediately shuts itself down, eliminating the build-up of zombie ADE processes.

To obtain your process ID from a .NET application, use the GetCurrentProcess().Id found in theSystem.Diagnostics.Process namespace. In other languages, you can use the Windows SDK function GetProcessId( ).

MonitorProcess() can only be used to monitor processes running on the same computer as ADE’s process, so you can’t use this if running ADE through DCOM. There is no need to use this when using the in-process ADEW server.

Opening a model with ADE

We will now open the Txc.ana model, and show the main window of our application. Use the following call:

theModelString = adeEngine.OpenModel(theModel)
frmMain.DefInstance.Show

The OpenModel function of CAEngine opens the model. If successful, the variable theModelString contains the name of the model. Otherwise, it contains an empty string. Although we haven’t done so in this example for the sake of brevity, you should check to see that the string returned from OpenModel isn’t empty. If it is, there was an error in opening your model. You can find out what kind of error with the ErrorCode and ErrorText properties of CAEngine (adeEngine.ErrorCode and adeEngine.ErrorText). We will see how to use these two properties later on. For a listing of all the error codes, see ADE Error Codes.

Retrieving objects from the Analytica model

The next step is to retrieve objects (variables, modules, functions, etc.) from our model, so that we can access their attributes (definition, title, class, etc.). Our example model (Txc.ana) manipulates the Pop_exp and Cost objects. In particular, it modifies Pop_exp to see how this effects the Cost object.

The PrintAttributes function in the file frmMain.frm of our TxcTest.vbproj (TxcText.sln) project shows how to do this. This function is first called by the Form_Load function of frmMain.frm, when the application starts, to display the Cost table. It is also called whenever we wish to print out the current result of our Cost table. The function looks like this:

Public Sub PrintAttributes(ByRef inputIdentifier As String, ByRef outputIdentifier As String)
Dim inputObject, outputObject As CAbject
Dim resultTable As CATable
Dim definitionAttrInput As String
inputObject = adeEngine.GetObjectByName(inputIdentifier)
outputObject = adeEngine.GetObjectByName(outputIdentifier)
definitionAttrInput = inputObject.GetAttribute("definition")
resultTable = outputObject.ResultTable
Call PrintResultTable(resultTable, inputIdentifier, definitionAttrInput, outputIdentifier)
ReleaseComObject(resultTable)
ReleaseComObject(inputObject)
ReleaseComObject(outputObject)
End Sub

PrintAttributes works with the variable identifiers Pop_exp passed as parameter inputIdentifier and Cost passed as parameter outputIdentifier. It fetches the corresponding objects using the GetObjectByName function of CAEngine as follows:

inputObject = adeEngine.GetObjectByName(inputIdentifier)
outputObject = adeEngine.GetObjectByName(outputIdentifier)

If GetObjectByName succeeds, it returns an object of type CAObject. You then use the functions of CAObject. See SendCommand(command) for a listing all CAObject functions. If GetObjectByName fails, the return value is Nothing. The code should check to make sure that the result from GetObjectByName is valid. If not, use the ErrorCode and ErrorText properties of CAEngine to get more information about the error. For example:

Set inputObject = adeEngine.GetObjectByName(inputIdentifier)
If inputObject Is Nothing Then
MsgBox(“This error from GetObjectByName occurred: “& _ vbCrLf & adeEngine.errorCode & “:” & adeEngine.errorText)
Else
'inputObject valid
End If

Getting object attributes

Each Analytica object has a set of attributes (analogous to properties), such as identifier, title, description, and class. You can use the GetAttribute function to obtain an attribute from an Analytica object. For example, to get the definition of inputObject (currently, the cost):

definitionAttrInput = inputObject.GetAttribute("definition")

In the Txc.ana model, the definition of Pop_exp is "Normal (30M, 3M)" which we store in definitionAttrInput.

Evaluating objects and retrieving results

Use Result or ResultTable methods of CAObject to get the value of a variable. ADE automatically evaluates the variable first, if necessary. Use the Result method if you are sure the result will be atomic, i.e., a single element. Otherwise, use ResultTable, which retrieves the result as an array. An atomic result is treated as a special case of an array, one with zero dimensions. If the value is atomic, the method AtomicValue returns its single value as a number or string.

By default, Result and ResultTable return the mid value of the result, i.e., the result of ADE evaluating it as deterministic. For a probabilistic value, set the ResultType property of CAObject to the desired uncertainty view — Mean, Sample, PDF, CDF, Confidence bands, or Statistics (see ResultType for details). We get the value of outputObject like this:

resultTable = outputObject.ResultTable

The result is a CATable object, which lets us access individual elements in a table.

If you call Result to get an array (or table) value, it returns the array as a string, listing the indexes and elements separated by commas. It is usually easier to use ResultTable, so that you don’t have to parse elements of the table from the string.

Getting the index elements of a table

An Analytica table has zero or more indexes. If it has one index, then it is one-dimensional; if it has two indexes, it is two-dimensional, and so on. A zero-dimensional table holds a single atomic (or scalar) value. You can use the NumDims function of CATable to get the number of dimensions (same as number of indexes) of a table. To get at the individual indexes of a table, use methods IndexNames and GetIndexObject of CATable.

The function PrintResultTable in frmMain.frm shows the use of these two functions. PrintResultTable is called from PrintAttributes, and does the actual work of printing the table that shows up in our TestTxc application (for brevity, we show only the parts of this function related to ADE).

Public Sub PrintResultTable(ByRef resultTable As CATable, ByRef inputIdentifier As String, ByRef definitionAttrInput As String, ByRef outputIdentifier As String)
Dim theIndexName, theTableName As String
Dim theIndexElement As String
Dim theTableElement
Dim theIndexObj As CAIndex
Dim numEls As Integer
Dim spaces, i As Integer
Dim lenStr As Short
Dim OutputStr As Short
Dim spaceString, underlineString As String
...
theIndexName = resultTable.IndexNames(1)
theTableName = resultTable.Name
theIndexObj = resultTable.GetIndexObject(theIndexName)
numEls = theIndexObj.IndexElements
...
For i = 1 To numEls
theIndexElement = theIndexObj.GetValueByNumber(i)
theTableElement = resultTable.GetDataByElements(i)
...
Next i
InformationPane.Text = outputString
End Sub

The lines of PrintResultTable that get an index of a table are as follows:

theIndexName = resultTable.IndexNames(1)
theIndexObj = resultTable.GetIndexObject(theIndexName)

We get the name of first index using the IndexNames function of CATable. We pass it into the GetIndexObject function of CATable to get a CAIndex object that represents our index. This automation object returns information about its corresponding index. If this function fails, it returns Nothing. In that case, use ErrorCode and ErrorText functions of CAEngine to find out why.

Getting information from CATable and CAIndex

Controlling formats of atomic values

Other ways to access tables

Modifying objects

Graphing with ADE

Conclusion

ADE User Guide Navigation
Installation of ADE << Previous ADE User Guide Next >> Using the Analytica Decision Engine Server
Comments


You are not allowed to post comments.