---
title: "RNetica Basics"
author: "Russell Almond"
date: "June 3, 2021"
output:
  pdf_document: default
  ioslides_presentation: default
  slidy_presentation: default
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
# RNetica and Peanut Packages

## Four Packages

![Peanut Package Dependencies](img/PnetPackages.png)

1. CPTtools -- Design patterns for Conditional Probability Tables
    * Independent of other packages
2. RNetica -- Low-level R/Netica Interface
    * Needs license from Norsys
3. Peanut -- Object-oriented parameterized Bayesian Networks
    * Implementation independent
    * Uses CPTtools
4. PNetica -- RNetica implementation of Peanut protocols

https://pluto.coe.fsu.edu/RNetica

_Packages must be installed in this order!_

## RNetica License Agreements

- R:  GPL-3 (Free and open source)
- RNetica, CPTtools, Peanut, PNetica:  Artistic-2.0 (Free and open source)
- Netica.dll/libNetica.so:  Commercial (openAPI, but not open source)
    * Free Student/Demo Version
        + Limited number of nodes.
        + Limited Usage (education, evaluation of Netica)
    * Paid version (see http://www.norsys.com/ for price information).
        + Need to purchase API, not GUI verison of Netica for RNetica
        + May want GUI for network visualization.
    * Netica.dll/libNetica.so included with binary distributions by permission of Norsys
        + License key still must be purchased.

## Installation Preliminaries

You can install the packages from source or (Windows and Mac OS only)
from precompiled binaries.    

Compiling packages from source require installing the proper
compilers.  Instructions can be found at
[Windows](https://cloud.r-project.org/bin/windows/Rtools/) or [Mac
OS](https://mac.r-project.org/tools/). 

The following packages are used by the PNetica software and should be installed (usual CRAN mirror) before starting.

```{r dependencies, eval=FALSE}
install.packages(c("R.utils","futile.logger","devtools",
                   "shiny","shinyjs", "ggplot2", "lattice", "devtools"),
                 repos="https://cloud.r-project.org/")
```
You can also install these packages using the RStudio `Tools > Install Packages` menu item. 

## Installation Method 1 -- Precompiled tarballs

This method is available only for Windows and Mac OS.


Download the compiles packages (`.zip` files for Windows, `.tgz` files for MacOS) from https://pluto.coe.fsu.edu/RNetica  (follow the links there for the latest version).  Make sure to grab all four tarballs.

Windows :
[CPTtools_0.7-2.zip](https://pluto.coe.fsu.edu/RNetica/CPTtools_0.7-2.zip) 
[RNetica_0.8-4.zip](https://pluto.coe.fsu.edu/RNetica/RNetica_0.8-4.zip) 
[Peanut_0.8-4.zip](https://pluto.coe.fsu.edu/RNetica/Peanut_0.8-4.zip)  
[PNetica_0.8-5.zip](https://pluto.coe.fsu.edu/RNetica/PNetica_0.8-5.zip)  

MacOS:
[CPTtools_0.7-2.tgz](https://pluto.coe.fsu.edu/RNetica/CPTtools_0.7-2.tgz) 
[RNetica_0.8-4.tgz](https://pluto.coe.fsu.edu/RNetica/RNetica_0.8-4.tgz)
[Peanut_0.8-4.tgz](https://pluto.coe.fsu.edu/RNetica/Peanut_0.8-4.tgz)
[PNetica_0.8-5.tgz](https://pluto.coe.fsu.edu/RNetica/PNetica_0.8-5.tgz)

## install.packages and R CMD INSTALLATION 


To install the packages using R Studio, select the "Tools > Install Packages" menu item, then select "Local Package Archive" from the first "Install from" drop down.  

You can also use this command:

```{r install local file, eval=FALSE}
install.packages(file.choose())
```

Finally, if you open a terminal window, you can run the command `R CMD INSTALL <packagefile>` to install the package.

_Packages must be installed in order!_

## Installation Method II -- Source and Github

All of the packages are currently available on [Github](https://github.com/ralmond).  

Installing RNetica from github requires the appropriate compilers (Rtools or Xcode) installed on your system.

```{r github installation, eval=FALSE}
#uncomment first line if needed.
#install.packages("devtools",repos="https://cloud.r-project.org")
library(devtools)
install_github("ralmond/CPTtools")
install_github("ralmond/RNetica")
install_github("ralmond/Peanut")
install_github("ralmond/PNetica")
```

## Netica Session:  R and Netica Memory

![Netica Session and Netica Heap](img/R--Netica.png)


- R and Netica have two different workspaces (memory heaps)
- R workspace is saved and restored automatically when you quick and restart R.
- Netica heap must be reconnected manually.

## Netica Session Object

![Netica Session and Netica Heap](img/R--Netica.png)

+ Provides link to Netica Memory Space
+ Is a container for all `NeticaNetwork` objects
+ Must be reconnected every R session, using `startSession()`

* When you purchase a license, Norsys will send you a license key.
  Something that looks like:
  
  “+Course/FloridaSU/Ex15-05-30,120,310/XXXXX” 
  
  (Where I’ve obscured the last 5 security digits) 

To install the license key, start R in your project directory and type:

```{r saveLicense, eval=FALSE}
NeticaLicenseKey <- “+Course/FloridaSU/Ex15-05-30,120,310/XXXXX”
q(“yes”)
```
Can also put first line in file in your home directory and run
`source("~/NeticaLicense.R")` every time you run RNetica.

## Starting RNetica

After you load RNetica you need to start the session.  This is when you pass the license key.

```{r Start with license, eval=FALSE}
library(RNetica)
sess <- NeticaSession(LicenseKey=NeticaLicenseKey)
startSession(sess)
```

```{r Start without license, eval=TRUE}
library(RNetica)
sess <- NeticaSession()
startSession(sess)
```
+ If license key is not installed, then you will get the
limited/student mode.  Most of these examples will run. 

## When to use the session object.

* When starting/restarting Netica
* When creating a network, or reading one from a file.
* When searching for networks.
* Certain global properties

`NeticaNetwork` objects have a `$session` proprty which points back to
the session. 

`NeticaNode` objects have a `$net` property which points back to the
network (which points to the session). 

## Active and Inactive Pointers

![Netica Session and Netica Heap](img/R--Netica.png)

+ When RNetica creates/finds a Netica object it creates a corresponding R object
+ R NeticaBN objects live in the NeticaSession object.  
    - R NeticaNode objects live in the NeticaBN.
+ If the pointer gets broken (saving & restarting R, deleting the network/node) then the R object becomes inactive.
+ The function `is.active()` tests to see if the node/net/session is active

## A simple example

A subset of the ACED (http://ecd.ralmond.net/ecdwiki/ACED/ACED/) network.

![ACED Motif 1](img/ACEDMotif1.png)

## Mini-ACED EM Fragments

All ACED tasks were scored correct/incorrect

Each evidence model is represented by a fragment consisting of observables with _stub_ edges indicating where it should be _adjoined_ with the network.

![Common Ratio Easy](../figures/CommonRatioEasyEM.png)

![Model Table Extend Hard](../figures/ModelTableExtendHardEM.png)

## Task to EM map

Need a table to tell us which EM to use with which task

```{r EM mapping}
## Read in task->evidence model mapping
EMtable <- read.csv("miniACED/MiniACEDEMTable.csv",row.names=1,
                    as.is=2) #Keep EM names as strings
EMtable
```

# Scoring Script

## Preliminaries

The following script assumes that the data files it needs are in the folder `miniACED` and that this folder is in the same directory as this `Rmd` file.

Don’t forget to `setwd()` to the `miniACED` folder (as it needs to find its networks).


```{r Start Netica}
## Scoring Script
## Preliminaries
library(RNetica)

sess <- NeticaSession()
startSession(sess)
```

## Read in the Network.

```{r read networks}
## Read in network -- Do this every time R is restarted
profModel <- ReadNetworks("../Nets/miniACEDPnet.dne",session = sess)
## If profModels already exists could also use

## Reconnect nodes -- Do this every time R is restarted
allNodes <- NetworkAllNodes(profModel)
sgp <- allNodes$SolveGeometricProblems
sgp
```

## Aside 1 -- Node Sets

Node sets can be viewed as either 
  
  A. a set of labels assigned to each node.
  
  B. a set of nodes which have a particular label.

In RNetica, these are very useful as they define collections 
of nodes that might be interesting in some way (e.g., 
Proficiency variables, Observable variable, background variables)

Node set operations yeild a list of nodes; 
iterating over that set is often very useful.

## Node Set Examples

```{r Node Sets}
## Node Sets
NetworkNodeSets(profModel)
NetworkNodesInSet(profModel,"pnodes")
```

## More Node Set Examples

```{r More Node Set Examples}
profNodes <- NetworkNodesInSet(profModel,"Proficiencies")
NodeSets(sgp)
```

Adding a node to a set.

```{r Setting node sets}
## These are all settable
NodeSets(sgp) <- c(NodeSets(sgp),"HighLevel")
NodeSets(sgp)
```


## Aside 2:  Common Net operations

Just about everything that can be done through the Netica GUI, 
can be done through the Netica API, and hence through R Netica.
[In practice, the API version has lagged the GUI version, and my 
RNetica release lag Norsys's API updates.]  Many more examples are in the RNetica help.  

```{r RNetica operations}
## Querying Nodes
NodeStates(sgp)   #List states
NodeParents(sgp)  #List parents
```
## More RNetica Queries

```{r More RNetica Operations}
NodeLevels(sgp)   #List numeric values associated with states
NodeProbs(sgp) # Conditional Probability Table (as array)
## These are all settable (can be used on RHS of <-) for model
## construction
```

## Conditional Probability Tables (as Data Frame)

```{r CPT}
sgp[] # Conditional Probability Table (as data frame)
```

Can use `[]` operator to select rows or elements 

Can set table or (row or cell).

`CPTtools` package has tools for building tables.
```{r CPTtools, eval=FALSE}
help(package="CPTtools")
```



## Inference

Networks must be _compiled_ before they are used for inference.

```{r Inference operations}
## Inference
CompileNetwork(profModel) #Lightning bolt on GUI 
## Must do this before inference
## Recompiling an already compiled network is harmless
```

```{r Values}
## Enter Evidence by setting values for these functions
NodeValue(sgp) #View or set the value
NodeLikelihood(sgp) #Virtual evidence
```

## Beliefs (Marginal Probabilities)

```{r Beliefs}
## Query beliefs
NodeBeliefs(sgp) #Current probability (given entered evidence)
NodeExpectedValue(sgp) #If node has values, EAP
## These aren't settable
```

```{r Retracting evidence}
## Retract Evidence
RetractNodeFinding(profNodes$ExamplesGeometric)
RetractNetFindings(profModel)
```

## Example:  Enter Evidence
```{r Enter Evidence}
## Enter Evidence
NodeFinding(profNodes$CommonRatio) <- "Medium"
## Enter Evidence "Not Low" ("High or Medium")
NodeLikelihood(profNodes$ExamplesGeometric) <- c(1,1,0)

NodeBeliefs(sgp) #Current probability (given entered evidence)
NodeExpectedValue(sgp) #If node has values, EAP
```

## Example:  Retract Evidence
```{r Retract Evidence}
## Retract Evidence
RetractNetFindings(profModel)
```


Many more examples:  
```{r help RNetica, eval=FALSE}
help(RNetica)
```


## Back to work

Simple Scoring Example 

*Start New Student*
Copy the _proficiency model_ to make _student model_.

```{r New Student}
Fred.SM <- CopyNetworks(profModel,"Fred")
Fred.SMvars <- NetworkAllNodes(Fred.SM)
CompileNetwork(Fred.SM)
```

## Setup score history.
```{r Setup history}
prior <- NodeBeliefs(Fred.SMvars$SolveGeometricProblems)
Fred.History <- matrix(prior,1,3)
row.names(Fred.History) <- "*Baseline*"
colnames(Fred.History) <- names(prior)
Fred.History
```


## Fred does a task

Task name and data.
```{r task data}
t.name <- "tCommonRatio1a"
t.isCorrect <- "Yes"
```

Adjoin SM and EM
```{r build motif}
EMnet <- ReadNetworks(file.path("..","Nets",
     paste(EMtable[t.name,"EM"],"dne",sep=".")),
                      session = sess)
obs <- AdjoinNetwork(Fred.SM,EMnet)
names(NetworkAllNodes(Fred.SM)) 
## Fred.SM is now the Motif for the current task.
CompileNetwork(Fred.SM)
```

## Absorb Evidence

Enter finding
```{r enter finding}
NodeFinding(obs$isCorrect) <- t.isCorrect
```

Calculate statistics of interest
```{r calculate statistics}
post <- NodeBeliefs(Fred.SMvars$SolveGeometricProblems)
Fred.History <- rbind(Fred.History,new=post)
rownames(Fred.History)[nrow(Fred.History)] <- paste(t.name,t.isCorrect,sep="=")
Fred.History
```


## Cleanup 

Network and Observable no longer needed, so absorb it:
```{r Absorb evidence nodes}
DeleteNetwork(EMnet) ## Delete EM
try(AbsorbNodes(obs))
## Currently, there is a Netica bug with Absorb Nodes, we will
## leave this node in place, as that is mostly harmless.
```
## 2nd Task

Write a script for scoring the second task.  
This time Fred attempts the task `tCommonRatio2a` and gets it incorrect.


```{r exercise, eval=FALSE}
### Fred does another task
t.name <- "tCommonRatio2a"
t.isCorrect <- "No"

## Load Evidence Model and adjoin

## Recompile

## Add Evidence

## Check Finding and add to history

## Clean up

```


## Answer for 2nd Task

```{r solution}
### Fred does another task
t.name <- "tCommonRatio2a"
t.isCorrect <- "No"

EMnet <- ReadNetworks(file.path("..","Nets",
                      paste(EMtable[t.name,"EM"],"dne", 
                            sep=".")),
                      session=sess)
obs <- AdjoinNetwork(Fred.SM,EMnet)
#NodeVisPos(obs$isCorrect) <- EMtable[t.name,c("X","Y")]
## Fred.SM is now the Motif for the current task.
CompileNetwork(Fred.SM)

NodeFinding(obs[[1]]) <- t.isCorrect
post <- NodeBeliefs(Fred.SMvars$SolveGeometricProblems)
Fred.History <- rbind(Fred.History,new=post)
rownames(Fred.History)[nrow(Fred.History)] <- 
      paste(t.name,t.isCorrect,sep="=")
Fred.History

## Cleanup:  Delete EM and Absorb observables
DeleteNetwork(EMnet) ## Delete EM
AbsorbNodes(obs)
```



## Fred does another task

```{r new data}
t.name <- "tCommonRatio2a"
t.isCorrect <- "No"
```

```{r new EM}
EMnet <- ReadNetworks(file.path("..","Nets",
                paste(EMtable[t.name,"EM"],"dne",sep=".")),
                session=sess)
obs <- AdjoinNetwork(Fred.SM,EMnet)
(NetworkAllNodes(Fred.SM)) ## Fred.SM is now the Motif for the current task.
CompileNetwork(Fred.SM)
```

## Task 2 continued

```{r enter new finding}
NodeFinding(obs[[1]]) <- t.isCorrect
post <- NodeBeliefs(Fred.SMvars$SolveGeometricProblems)
Fred.History <- rbind(Fred.History,new=post)
rownames(Fred.History)[nrow(Fred.History)] <- paste(t.name,t.isCorrect,sep="=")
Fred.History
```


Cleanup:  Delete EM and Absorb observables
```{r cleanup on task 2}
DeleteNetwork(EMnet) ## Delete EM
try(AbsorbNodes(obs))
## Currently, there is a Netica bug with Absorb Nodes, we will leave
##this the node in place as that is mostly harmless.
```


## Fred logs out
Save network to a file.
```{r Save Fred}
WriteNetworks(Fred.SM,"FredSM.dne")
DeleteNetwork(Fred.SM)
is.active(Fred.SM)  ## No longer active in Netica space
```

Fred logs back in
```{r Restor Fred}
Fred.SM <- ReadNetworks("FredSM.dne",session=sess)
is.active(Fred.SM)
```


# Score an entire set of cases.

## Read in the scores.

```{r miniACED data}
miniACED.data <- read.csv("../Nets/miniACED-Geometric.csv",row.names=1)
head(miniACED.data)
```
Some meta-data
```{r metadata}
## Mark columns of table corresponding to tasks
first.task <- 9
last.task <- ncol(miniACED.data)
## Code key for numeric values
t.vals <- c("No","Yes")

## Pick a student, we might normally iterate over this.
Student.row <- 1
```

## Setup for student in sample

Create Student Model from Proficiency Model
```{r setup student}
Student.SM <- CopyNetworks(profModel,"Student")
Student.SMvars <- NetworkAllNodes(Student.SM)
CompileNetwork(Student.SM)
```

Initialize history list
```{r setup history}
prior <- NodeBeliefs(Student.SMvars$SolveGeometricProblems)
Student.History <- matrix(prior,1,3)
row.names(Student.History) <- "*Baseline*"
colnames(Student.History) <- names(prior)
```

## Now loop over tasks

```{r Scoreit}
for (itask in first.task:last.task) {
  
  ## Look up the EM for the task, and adjoin it.
  tid <- names(miniACED.data)[itask]
  EMnet <- ReadNetworks(file.path("..","Nets",
                        paste(EMtable[tid,"EM"],"dne",sep=".")),
                        session=sess)
  obs <- AdjoinNetwork(Student.SM,EMnet)
  CompileNetwork(Student.SM)

  ## Add the evidence
  t.val <- t.vals[miniACED.data[Student.row,itask]] #Decode integer
  NodeFinding(obs[[1]]) <- t.val
  
  ## Update the history
  post <- NodeBeliefs(Student.SMvars$SolveGeometricProblems)
  Student.History <- rbind(Student.History,new=post)
  rownames(Student.History)[nrow(Student.History)] <- paste(tid,t.val,sep="=")

  ## Cleanup, Delete EM and Absob Observables
  DeleteNetwork(EMnet)
  try(AbsorbNodes(obs)) # Still broken
}
```

## Now look at the scoring history.
```{r history}
Student.History
```

# Weight of Evidence
## Weight of Evidence

Good \(1985\)

_H_ is binary hypothesis\, e\.g\.\, _Proficiency_ > _Medium_

_E_ is evidence for hypothesis

Weight of Evidence \(WOE\) is

$$ W(H:E) = \log \frac{P(E|H)}{P(E|\overline{H})} =
\log  \frac{P(H|E)}{P(\overline{H}|E)} -
\log  \frac{P(H)}{P(\overline{H})} $$

## Conditional Weight of Evidence

$$ W(H: E_2 | E_1) = \log  \frac{P(E_2|H,E_1)}{P(E_2|\overline{H},E_1)}
$$
Additive properties

$$ W(H: E_1, E2) = W(H: E_1) + W(H: E_2|E_1) $$

Order senstive (evidence seen earlier is worth more)

WOE Balance Sheet:

## Now examine scoring history

```{r woe balance sheet}
woeBal(Student.History,c("High","Medium"),"Low",
       title=paste("Evidence Balance Sheet for ",
                   rownames(miniACED.data)[Student.row]))
```

## For More information

```{r More Help, eval = FALSE}
help(RNetica)
help(package="RNetica")
help(CPTtools)
help(package="CPTtools")
```
