ADE 入門教程
此教程教你如何在一個Visual Basic程序中使用Analytica決策引擎(ADE)。
你的第一個ADE應用程式
首先先從頭開始寫一個簡單的ADE程序,只要確認一切設置正常。按照以下步驟操作:
test
第一個程序執行了以下操作:
- 建立一個名稱為ADE的CAEngine自動化對象(通過使用
new CAEngine
). - 打開Analytica模型 (通過使用CAEngine的OpenModel 方法。
- 顯示模型名稱返回OpenModel的值。
我們將在後面的章節詳細了解這些信息。
接下來講什麼呢?
我們不會嘗試在此入門教程中解釋ADE的特徵。本指南後面幾個章節會描述這些特徵。
從現在開始,我們我們將使用名稱為Txc.ana的示例模型。打開 Analytica安裝目錄下 Example Models文件夾裡面的Risk Analysis文件夾便可找到Txc.ana文件。如果你找不到,或者你安裝Analytica的時候未選擇安裝examples(示例)文件,在ADE安裝目錄下面的Examples\Tutorial.NET文件家中有一個副本。
Txc模型演示了減少污染物TXC排放的risk-benefit (風險-效益)分析。請用Analytica打開Txc查看它是如何工作的。
ADE Examples\Tutorial.NET 文件夾中名稱為TestTxc的Visual Basic.NET程序示例顯示了ADE很多方面。該程序建立了一個ADE自動化對象,打開包含該對象的Txc.ana模型,獲取變量 Population Exposed的定義,計算變量Total Cost,通過獲取每個表格的單獨組件以表格的方式列印輸出變量Total Cost 的結果,然後改變變量Population Exposed 的定義。再次獲取變量Total Cost 的值,看一下 變量Population Exposed定義的改變對變量Total Cost有什麼影響。如果設置正確的話,TestTxc將顯示在「Text Txc window」中顯示的窗口。
應用程式顯示變量Population Exposed 的定義("Normal (30M, 3M)"),以及與Total Cost關聯的表格,這都基於Population Exposed的定義。你可以通過從主菜單選擇File > Change Population Exposed改變Population Exposed的定義,然後查看改變對Total Cost表格的影響。
區分title(名稱) 和 identifier(標識符)
無論什麼時候,一個ADE函數都需要一個變量你必須傳達變量的標識符 ,而不是它的名稱。這可能會引起混淆,因為在Analytica影響圖中一般顯示每一個變量的名稱。默認情況下,在你最初建立每一個對象的時候,Analytica自動根據名稱建立一個標識符。用(_)代替名稱中的每一個空格或者其他非字母和數字字符。
你可以通過點擊「Control+y」(或者從Object(對象)菜單選擇Show by identifier :顯示標識符) 來以標識符顯示變量。對於模型Txc.ana,你可以看到名稱為Population Exposed變量的標識符為 Pop_exp。將變量傳遞給ADE函數時,使用標識符很重要。如果你傳遞Population Exposed,ADE無法找到變量,並且將返回一個錯誤。
從Visual Basic中建立一個ADE 對象
如果你還沒準備好,將名稱為tt>Examples\Tutorial\TestTxc.sln載入到Visual Basic.NET中,查看名稱為TestTxc.vb的文件的代碼。其代碼看起來像:
- Imports ADEW
- . . .
- Public adeEngine As CAEngine
- Public Sub Main()
- End Sub
在每一個文件的頂部的代碼將自動化對象 adeEngine 聲明為一個CAEngine 對象。使用此對象,我們能夠訪問CAEngine 展示的所有公共函數(參考 ADE伺服器類型參考 )
變量adeEngine包含我們的進程內對象CAEngine。如果我們想使用ADE的本地(進程外)伺服器版本,我們可以將一個項目參考添加到Analytica Decision Engine Server 4.6 COM 組件並將頂部行從Imports ADEW改成Imports ADE即可。
這裡是另一種獲得一個先CAEngine對象的方法。此循序不需要給項目添加參考。
- adeEngine = CreateObject("ADEW4.6.CAEngine") 』 in-process
- adeEngine = CreateObject("ADE4.6.CAEngine") 』 out-of-process
如果想理解使用進程內和進程外(或本地)伺服器的利與弊,以及哪一種自動化伺服器用於不同的場景,參考 In-process vs. out-of-process
COM和 Automation 接口比較
在上面的例子當中,我們使用COM接口調用ADE。在一個COM 接口中,對象 在本例中為CAEngine 被聲明為CAEngine ,編譯器解決每一個成員函數,並且在編譯時間內能探測幾個明顯的錯誤。另外,Visual Studio可以提供方法和參數類型列表當,在你編程的時候可以當做一個提示工具,在編寫使用ADE程序是非常有用。COM調用比Automation調用要快一點,但是在ADE應用程式中速度差異通常不是很重要。對於ADE 4.6我們推薦使用COM結構,只要你的語言支持。
在VB Automation中,你可以將一個對象簡單聲明為Object(對象),而不是像CAEngine、CAObject等更加具體的類型。當使用Automation調用ADE方法時,在運行時間內確定方法。在編譯時間內,編譯器不知道你的 ADE 對象是否有一個名稱為OpenModel的函數。在VB中,調用COM方法或Automation 方法的語法是相通的——唯一的不同在於對象類型是否明確被聲明。
在VC++和C#中,調用COM的語法和調用Automatio的語法不同。在這些情況下,COM要方便的多,Automation能夠獲得相冗餘。然而,同樣的語言,包括VBScript和其他腳本語言,只支持Automation不支持COM。
監視Process(進程)
當使用進程外ADE伺服器時,你的代碼必須在終止時釋放CAEngineCOM對象。當最終CAEngine使用被釋放時,ADE.exe 進程自動終止。到目前為止所看到的代碼,VB語言在達到程序末端時,自動處理釋放對象。但是,當你調試你的代碼是,你可以提前終止你的程序來修復bug,你的程序可能被Task Manager(任務管理器)終止,或者你自己的代碼也可能奔潰,導致你的程序進程在它有機會釋放對象之前終止。因為COM對象從來不會被釋放,ADE.exe進程不知道它是否在使用中,你的ADE.exe 進程沒有任何進展。
為了避免這種情況發生,一種好的習慣就是在調用CAEngine實例後 立即調用CAEngine::MonitorProcess。通過這種方式,ADE能夠得知哪一個進程正在使用它,同時將設置一個程序去探測你的進程是否在ADE被完全釋放前終止。如果你的程序進程沒有終止,ADE進程會立即將它關閉,消除了ADE沒有進展的應用。
要從一個.NET應用程式獲得進程ID,使用System.Diagnostics.Process namespace中的GetCurrentProcess().Id 。在其他語言中,你可以使用Windows SDK 的GetProcessId( )函數。
MonitorProcess() 只能用來監視運行ADE進程的同一台計算機的進程,一次當通過DCOM運行ADE時不能使用。當使用進程內ADEW伺服器時沒有必要使用。
使用ADE打開模型
我們現在打開Txc.ana模型,顯示我們的應用程式主窗口。使用以下調用:tt>
- theModelString = adeEngine.OpenModel(theModel)
- frmMain.DefInstance.Show
使用CAEngine的OpenModel函數打開模型。如果成功的話,變量theModelString將包含模型的名字。否則,將包含一個空的串。雖然由於為了簡介的緣故,我們沒有這樣做,但是你應該檢查從OpenModel函數返回的是不是一個空的串。如果是,說明在你打開的模型中存在一個錯誤。你可以使用CAEngine的ErrorCode和ErrorText屬性找出是哪種錯誤 (adeEngine.ErrorCode 和adeEngine.ErrorText)。
從Analytica模型中檢索對象
下一步就是從我們的模型中檢索對象(變量、模塊、函數等),以便我們能獲取他們的屬性(定義、名稱、類型等等 )。我們的示例模型(Txc.ana)操作Pop_exp和Cost對象。尤其是,修改Pop_exp可以查看這是如何影響Cost 對象。
我們的TxcTest.vbproj (TxcText.sln)項目中的frmMain.frm文件里的 PrintAttributes 函數演示了如何檢索對象。該函數首先被frmMain.frm中的Form_Load 函數調用,當應用程式啟動後,可以顯示Cost表格。當然無論什麼時候,只要我們想輸出Cost表格的當前值,都可以調用。該函數如下所示:
- Public Sub PrintAttributes(ByRef inputIdentifier As String, ByRef outputIdentifier As String)
- Dim inputObject, outputObject As CAObject
- 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和作為inputIdentifier參數的Pop_exp變量標識符以及作為outputIdentifier參數的Cost變量一起使用。如下所示通過使用CAEngine的GetObjectByName函數提取相應對象:
- inputObject = adeEngine.GetObjectByName(inputIdentifier)
- outputObject = adeEngine.GetObjectByName(outputIdentifier)
如果函數GetObjectByName提取成功,將返回一個CAObject類型對象。如果使用CAObject的函數,參考SendCommand(command),可以獲得一個CAObject的完整函數列表。如果函數GetObjectByName提取失敗,返回值為Nothing。代碼應該檢查以確保GetObjectByName提取的結果是有效的。如果找不到,可使用CAEngine的 ErrorCode和 ErrorText屬性獲取錯誤信息。例如:
- Set inputObject = adeEngine.GetObjectByName(inputIdentifier)
- If inputObject Is Nothing Then
- Else
- 'inputObject valid
- End If
獲取對象屬性
每一個Analytica對象都有一組屬性,例如標識符、名稱、 描述和類型。你可以使用GetAttribute函數來獲去Analytica對象的屬性。例如,如要獲取inputObject的定義(當前為cost):
- definitionAttrInput = inputObject.GetAttribute("定義")
在模型Txc.ana 中,Pop_exp的定義為"Normal(30M, 3M)" ,我們將它儲存在definitionAttrInput當中。.
計算對象和檢索結果
使用CAObject的Result或ResultTable方法來獲取變量的值。如果有必要的話,ADE先自動計算變量。如果你確定結果為基元(只有一個元素),使用Result方法。否則使用ResultTable,檢索結果為一個數組。基元結果當作一個特殊數組案例來處理,沒有維度的數組。如果值為基元AtomicValue方法將返回一個數字或者字符串單一值。
默認情況下,Result和ResultTable返回結果的中值,也就是說,ADE以確定性方式計算結果。對於一個概率值,將CAObject的ResultType屬性設置為想要的不確定性視圖——Mean(平均值) Sample(樣本)、PDF(概率密度函數)、CDF(累計分布函數)、Confidence bands(置信帶)、或者Statistics(統計數據)詳情請參考ResultType。我們通過如下的方式獲取outputObject 輸出對象 的值:
- resultTable = outputObject.ResultTable
結果為一個CATable對象,允許我們訪問一個表格中的單個元素。
如果你通過調用Result來獲取一個數組(或者表格)的值,將返回數組為一個字符串,列出索引和元素,以逗號隔開。通常使用ResultTable更加簡單,這樣你沒有必要從字符串中解析表格的元素。
獲取表格索引元素
一個Analytica表格包含0個或者多個索引。如果包含一個索引,那麼該表格是1維表格;如果有2個索引,說他是2維表格,依此類推。0維度表格包含一個「單一基元」(或標量)值。你可以使用CATable的NumDims獲取表格的維數(也就是索引數)。要想獲取表格的單個索引,可使用CATable的 IndexNames和 GetIndexObject函數。
frmMain.frm中的PrintResultTable函數說明了這兩個函數的使用。從PrintAttributes中調用PrintResultTable,執行列印TestTxc中的表格的實際工作(為了簡潔起見,我們只給出了關聯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
通過PrintResultTable獲取一個表格的索引方法如下:
- theIndexName = resultTable.IndexNames(1)
- theIndexObj = resultTable.GetIndexObject(theIndexName)
我們通過使用CATable的IndexNames函數獲取第一個索引的名字。我們將它傳遞給CATable的GetIndexObject函數以獲取表示我們的索引的CAIndex。該自動化對象返回其相應索引的相關信息。如果此函數調用失敗,將返回Nothing信息。在此情況下,使用CAEngine的ErrorCode和ErrorText 函數找出原因。
從CATable和CAIndex中獲取信息
PrintResultTable說明了如何從 CATable和CAIndex 對象獲取信息。該代碼獲取Cost表格的索引和表格元素:
- numEls = theIndexObj.IndexElements
- For i = 1 To numEls
- theIndexElement = theIndexObj.GetValueByNumber(i)
- theTableElement = resultTable.GetDataByElements(i)
- ...
- Next i
CAIndex的IndexElements屬性返回第一個索引中的元素個數。CAIndex的GetValueByNumber函數獲取單個索引元素。
我們通過傳遞表格中元素的坐標,使用 CATable的GetDataByElements函數獲取Cost表格對象的單個表格元素resultTable。
當我檢索 CATable對象中單個元素(resultTable)的時候,我們利用了該表格是一維的事實。因此,我們只需要傳遞給GetDataByElements一個單一數(這個數代表表格中的位置)。但是,如果我們處理兩個或者更多維度,我們必須傳遞給GetDataByElements一個數組函數,並且指明我們要檢索的表格元素的位置。因此,如果我們不想檢索一個二維數組的position (4,3)位置的元素,我們可以這樣寫:
- Dim W as Variant 'return element
- Dim IndexPtrs(1 To 2) As Variant 'position in table
- ...
- IndexPtrs(1) = 4
- IndexPtrs(2) = 3
- W = resultTable.GetDataByElements(IndexPtrs)
控制基元值格式
CATable中的每個基元值可以是一個數、一個字符串,或者其它一些基本類型(例如:空值、未定義、參考,或 句柄。通過說明類型和值,它們作為變體被返回,這是一種Visual Basic能讀懂的數據結構。CATable的RenderingStyle屬性控制Analytica基礎值是如何映射到Visual Basic變體的。
例如:可以通過使用Analytica 模型的數字格式設置將一個數值返回成一個數字或者字符串。如果被指定格式,有一個選線控制是截斷數字的位數,還是精確返回。
子程序PrintResultTable在frmMain.vb中載入,其渲染風格直接指明:
- resultTable.RenderingStyle.NumberAsText = True
- resultTable.RenderingStyle.FullPrecision = False
- resultTable.RenderingStyle.StringQuotes = 2
第一行指明數值應該根據與結果對象有關的數字格式被格式化為文本。例如在其程序輸出中,我們將看到30.103M,而非30102995.6639812,如果我們讓Visual Basic將數值串聯我們的結果字符串,將顯示該結果。在此事件中,一個值為字符串的單元格出現在結果中,返回值將明確帶雙引號。對於CARenderingStyle對象中的所有可用屬性,請參考CARenderingStyle。
其它訪問表格的方式
有幾種訪問多維表格CATable中元素的方式。每一種都有可能在特定的場合使用起來比較方便。
第一種方式就是使用 CATable的 GetDataByElements或GetDataByLabels方法,上面代碼示例中有顯示。在此情況下,利用你想獲取的基元值的單元格位置。
第二種方式就是使用CATableSlice或 Subscript方法獲取一個少一個維度的新CATable對象。通過重複減少維度,最終將獲得0維度,這時你將獲得一個單一基元值。此時,CATable的AtomicValue方法返回該值 。AtomicValue方法是唯一獲取一個標值的方法(因為沒有坐標位置)。如果你需要建立完整結果其中一個切片的圖形圖像,必須使用此方法。
第三種方式就是使用CATable的GetSafeArray方法,將多維數組轉化成一個安全數組(或者一個.NET數組)。你可以在使用VB或者其它.NET語言直接處理多維數組。因為對於Analytica維度沒有固定順序,但是安全數組和.NET數組有一個明確的順序,你必須先使用CATable對象的SetIndexOrder方法在調用GetSafeArray之前指明維度的順序。注意如果你知道你的數組是一維的,那就沒必要了。
修改對象
一個自定義用戶程序通常從一個用戶或者其它外部源獲取輸入,以便轉化成Analytica模型中的輸入變量。你可以設定輸入變量的定義或者通過使用定義表格來這樣做。
TestTxc 說明了如何修改Pop_exp定義,此模型是一個影響結果變量Cost的模型輸入。要想在該示例中設定該定義,從主菜單上選擇File > Change Population。將出現一個對話框,如下圖所示:
在信息欄中輸入一個新定義並點擊Ok確認。主窗口將顯示一個新的Cost值。當點擊該對話框中的Ok按鈕時,調用了ChangeDef.frm函數,然後調用PrintAttributes函數列印Cost的結果。
此函數類似於:
- Private Sub OkButton_Click(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles OkButton.Click
- Dim errorText As String
- Dim pop_exp_Object As CAObject
- Dim errorCode As Short
- Dim errorString As String
- newDefinition = PopExposedDef.Text
- pop_exp_Object = adeEngine.GetObjectByName("pop_exp")
- pop_exp_Object.SetAttribute("Definition", newDefinition)
- errorCode = adeEngine.ErrorCode
- If errorCode <> 0 Then
- MsgBox("This error occurred while processing your definition: " & vbCrLf & vbCrLf & adeEngine.ErrorText) :::PopExposedDef.Focus()
- Else
- Me.Close()
- frmMain.DefInstance.PrintAttributes("Pop_exp", "Cost")
- End If
- ReleaseComObject(pop_exp_Object)
- End Sub
該函數抓取了輸入到New Definition 新定義給Population Exposed欄,並通過使用CAObject對象中的SetAttribute函數將它設置給Pop_exp對象。然後調用PrintAttributes,以計算Cost object,並列印新的結果。要想給變量pop_exp設定一個新定義,我們可以為Pop_exp獲取CAObject對象,然後將其定義設置成用戶輸入的定義。通過使用下面的代碼來實現:
- pop_exp_Object = adeEngine.GetObjectByName("pop_exp")
- pop_exp_Object.SetAttribute( "Definition", newDefinition )
無論什麼時候調用SetAttribute,都應該檢查CAEngine自動化對象(adeEngine)的ErrorCode,以免它的定義是非法的。嘗試一下輸入一個新定義,比如Uniform(25M,35M)
,然後點擊Ok。當pop_exp的定義被改變時, Cost的結果由ADE重新計算,此時ResultTable接下來調用給Cost變量(在應用程式窗口重新顯示時)。
使用ADE繪圖
通過Analytica 4.6使用相同的繪圖引擎,你可以建立一個圖表或者圖形以顯示一個數組值或者不確定值結果。在ADE4.6中,你可以使用 CATable的GraphToFile和GraphToStream方法。圖形可以以幾種可能的圖像格式返回,例如image/png、 image/bmp或者 image/jpeg。
從可用圖形選項中選擇的最簡單方式就是使用Analytica打開你的模型。你可以實驗設置各種默認設置或者替代選擇的變量,看它們是如何工作的。但你選擇了你想設置的選項,保存模型。當為每一個生成結果圖形時,ADE將使用這些設置。
對於更高維度的結果,可能需要做一些必要工作,以便選擇將繪製結果的切片和具體的軸(也就是相對於圖例哪個維度作為X軸)。CATable的Subscript或Slice方法可以用來控制軸選擇特殊的切片用來繪製,能夠用來控制軸。詳情請參考CATable。在我們的Tutorial.NET示例中,我們有一個一維結果(Cost),因此我們沒有必要擔心截取或者繞軸旋轉。
GraphToStream方法被用來從ADE中直接轉化圖形圖形到一個用戶界面中。使用GraphToStream比起使用GraphToFile來要稍微複雜一點,因為GraphToFile除了需要文件名以外還需要其它一些信息,以便編寫圖形。要使用GraphToStream,我們必須在內存內設置一個流,允許ADE寫入該留,然後從該留重建。因為.NET流和COM流不兼容,你必須使用ADE一同提供的StreamConnector類型.frmMain 中的GraphResult_Click子程序演示了GraphToStream的使用。從主應用程式窗口選擇 Graph Result菜單選項,結果將出現在一個如下所示的圖形當中:
結論
在此教程中,我們介紹了Analytica決策引擎的幾個重要方面。我們看到了如何建立一個ADE伺服器對象,使用ADE打開模型,獲取模型中的單個對象、計算對象、獲取表格中的元素,以及修改模型中的對象。但是ADE可以做的遠不止這些。
我們希望你已經足夠了解其基本特徵,這樣你現在才能獨自開始探索更多新的高級特徵。我們建議你現在開始閱讀字教程的剩餘部分以了解ADE能做什麼。
另請參考
ADE 安裝 <- | ADE 入門教程 | -> 使用ADE伺服器 |
Enable comment auto-refresher