/bayesjs

Inference on Bayesian Networks

Primary LanguageTypeScriptMIT LicenseMIT

Build Status Coverage Status npm bundle size Commitizen friendly js-standard-style semantic-release

BayesJS

A inference library for Bayesian Networks made with TypeScript.

Inferences

Currently there are three inferences algorithms:

Methods

infer(network: INetwork, nodes?: ICombinations, given?: ICombinations): number

Calculate the probability of a node's state.

This function receives a network, and will return the probability of the node having the given state subject to the given evidence.

As mentioned above, there are three inferences engines, by default the junction tree algorithm is used to execute the infer function.

If you want to perform repeated inferences from the same bayes network, you should consider instanciating an inference engine for that network.

import { infer, inferences } from 'bayesjs';

infer(network, event, evidence); // Junction tree algorithm

inferences.enumeration.infer(network, event, evidence);
inferences.variableElimination.infer(network, event, evidence);
inferences.junctionTree.infer(network, event, evidence);
Example

Given the rain-sprinkler-grasswet network. Image here.

import { infer } from 'bayesjs';

const network = // ...

// What is the probability that it is raining (RAIN = T)?
infer(network, { 'RAIN': 'T' }).toFixed(4) // 0.2000
// What is the probability that it is raining (RAIN = T), given the sprinkler is off (SPRINKLER = F)?
infer(network, { 'RAIN': 'T' }, { 'SPRINKLER': 'F' }).toFixed(4) // 0.2920

inferAll(network: INetwork, given?: ICombinations, options?: IInferAllOptions): INetworkResult)

Calculate the marginal distributions for a given network subject to some given evidence.

This method will execute the junction tree algorithm on each node's state.

Options
precision

default: 8

Rounds the network results according to this value. To round the value we are using round-to.

Some rounds examples:

  • 0.30000000000000004
    • 8 precision -> 0.3
    • 4 precision -> 0.3
    • 2 precision -> 0.3
  • 0.3333333333333333
    • 8 precision -> 0.33333333
    • 4 precision -> 0.3333
    • 2 precision -> 0.33
  • 0.9802979902088171
    • 8 precision -> 0.98029799
    • 4 precision -> 0.9803
    • 2 precision -> 0.98
Example
const network = {
  'Node 1': {
    id: 'Node 1',
    states: ['True', 'False'],
    parents: [],
    cpt: { True: 0.5, False: 0.5 },
  },
  'Node 2': {
    id: 'Node 2',
    states: ['True', 'False'],
    parents: [],
    cpt: { True: 0.5, False: 0.5 },
  },
  'Node 3': {
    id: 'Node 3',
    states: ['True', 'False'],
    parents: ['Node 2', 'Node 1'],
    cpt: [
      {
        when: { 'Node 2': 'True', 'Node 1': 'True' },
        then: { True: 0.5, False: 0.5 },
      },
      {
        when: { 'Node 2': 'False', 'Node 1': 'True' },
        then: { True: 0.5, False: 0.5 },
      },
      {
        when: { 'Node 2': 'True', 'Node 1': 'False' },
        then: { True: 0.5, False: 0.5 },
      },
      {
        when: { 'Node 2': 'False', 'Node 1': 'False' },
        then: { True: 0.5, False: 0.5 },
      },
    ],
  },
};

const evidence = { 'Node 1': 'True' }

inferAll(network, evidence)
// {
//   'Node 1': { True: 1, False: 0 },
//   'Node 2': { True: 0.5, False: 0.5 },
//   'Node 3': { True: 0.5, False: 0.5 },
// }

// Mutating the network...
network["Node 3"].cpt[0].then = { True: 0.95, False: 0.05 };

inferAll(network, evidence);
// {
//   'Node 1': { True: 1, False: 0 },
//   'Node 2': { True: 0.5, False: 0.5 },
//   'Node 3': { True: 0.725, False: 0.275 }
// }

IInferenceEngines)

Inference engines provide an efficient way of performing repeated inferences on the same Bayesian network. Currently there is one inference engine provided:

  • InferenceEngine) - An inference engine that uses the junction tree algorithm for infereneces, caching the network topology and potentials for efficiency.

Each engine can be ititialized by calling the constructor with the desired network. The example below shows how to perform efficient repeated inference on the Alarm bayes network.:

import { InferenceEngine } from 'bayesjs'
import { allNodes } from './models/alarm.ts'

const network = createNetwork(...allNodes )
const engine = InferenceEngine( network )

// Make multiple inferences with the network without inferences
console.log( engine.infer({ 'JOHN_CALLS': 'T' })) // 0.0521
console.log( engine.infer({ 'MARY_CALLS': 'T' })) // 0.0117
console.log( engine.getEvidence ) // { }

// inject some evidence and make multiple inferences
engine.setEvidence({ 'EARTHQUAKE': 'T' } )
console.log( engine.infer({ 'JOHN_CALLS': 'T' })) // 0.2971
console.log( engine.infer({ 'MARY_CALLS': 'T' })) // 0.2106
console.log( engine.getEvidence ) // { 'EARTHQUAKE': 'T' }

// Update the distribution for a node and make some inferences
engine.setDistribution('BURGLARY', { T: 0.05, F: 0.95 })
console.log( engine.infer({ 'JOHN_CALLS': 'T' })) // 0.3246
console.log( engine.infer({ 'MARY_CALLS': 'T' })) // 0.2329
console.log( engine.getEvidence ) // { 'EARTHQUAKE': 'T' }

// incrementally add additional evidence and make multiple inferences
engine.setEvidence({ 'ALARM': 'T' } )
console.log( engine.infer({ 'JOHN_CALLS': 'T' })) // 0.9000
console.log( engine.infer({ 'MARY_CALLS': 'T' })) // 0.7000
console.log( engine.getEvidence ) // { 'ALARM': 'T', 'EARTHQUAKE': 'T' }

// Remove all the evidence and make multiple inferences.
engine.removeAllEvidence()
console.log( engine.infer({ 'JOHN_CALLS': 'T' })) // 0.0912
console.log( engine.infer({ 'MARY_CALLS': 'T' })) // 0.0435
console.log( engine.getEvidence ) // { }

addNode(network: INetwork, node: INode): INetwork

Add a node in a Bayesian Network.

This function receives a network and a node, check if the node can be appended on the network. If something is wrong an exception will be thrown, otherwise, a new network will return with the node added.

Example
import { addNode } from 'bayesjs';

const networkWithRainAndSprinkler = // ...

const grassWet = {
  id: 'GRASS_WET',
  states: [ 'T', 'F' ],
  parents: [ 'RAIN', 'SPRINKLER' ],
  cpt: [
    { when: { 'RAIN': 'T', 'SPRINKLER': 'T' }, then: { 'T': 0.99, 'F': 0.01 } },
    { when: { 'RAIN': 'T', 'SPRINKLER': 'F' }, then: { 'T': 0.8, 'F': 0.2 } },
    { when: { 'RAIN': 'F', 'SPRINKLER': 'T' }, then: { 'T': 0.9, 'F': 0.1 } },
    { when: { 'RAIN': 'F', 'SPRINKLER': 'F' }, then: { 'T': 0, 'F': 1 } }
  ]
};

const newtwork = addNode(networkWithRainAndSprinkler, grassWet);

License

MIT