Git Product home page Git Product logo

bayesjs-editor's Introduction

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, a node's state, and the knowing states and will return the probability of the node's state give.

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

It's important to remember that junction tree uses WeakMap to cache some internal results, if you are mutating the network or given object is advisable to shallow clone both objects before infer. Read more about JT cache here

import { infer, inferences } from 'bayesjs';

infer(network, nodes, give); // Junction tree algorithm

inferences.enumeration.infer(network, nodes, give);
inferences.variableElimination.infer(network, nodes, give);
inferences.junctionTree.infer(network, nodes, give);
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 all probabilities from a network by receiving the network, knowing states, and options. It returns an object with all results.

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

Options
force

default: false

Enforces to clear junction tree cache before inferring all network. The junction tree uses WeakMap to store the cliques and potentials that are used at the algorithm.

  • cliques weak stored by network
  • potentials weak stored by cliques and given

This option is only necessary if you are mutation your network or given object instead of creating a new object before inferring each time.

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 given = { 'Node 1': 'True' }

inferAll(network, given)
// {
//   '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, given);
// Cached result - wrong
// {
//   'Node 1': { True: 1, False: 0 },
//   'Node 2': { True: 0.5, False: 0.5 },
//   'Node 3': { True: 0.5, False: 0.5 },
// }

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

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

bayesjs-editor's People

Contributors

abnersajr avatar anbusse avatar dependabot[bot] avatar edumoreira1506 avatar eliasreis54 avatar fhelwanger avatar iam-abbas avatar istumpf avatar netochaves avatar nolleto avatar ravitejacms avatar tanushree57 avatar ufocoder avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

bayesjs-editor's Issues

Test the base selectors

This issue can ben two PRs if you want but, do the tests first to make sure nothing breaks.

Just to be clear, you must add test and refactor this code:

export const getNetwork = state => state.network;
export const getNodes = state => state.network.nodes || state.nodes || [];
export const getPositions = state => state.network.positions || state.positions || [];
export const getBeliefs = state => state.network.beliefs;
export const getSubnetworks = state => state.network.subnetworks || [];
export const getSelectedNodes = pipe(getNetwork, prop('selectedNodes'));
export const getNetworkKind = state => state.network.kind || NETWORK_KINDS.BN;
export const getPanelVisibility = state => state.network.propertiesPanelVisible;
export const getLinkages = state => state.network.linkages;
export const getInferenceEnabled = (state) => {
  const { inferenceEnabled } = getNetwork(state);

  return inferenceEnabled === undefined ? true : inferenceEnabled;
};

Add info about baysian network into readme

Add a new section above What is Bayes' Theorem with:

## What is a [Bayesian network](https://en.wikipedia.org/wiki/Bayesian_network)

A Bayesian network is a probabilistic graphical model (a type of statistical model) that represents a set of variables and their conditional dependencies via a directed acyclic graph (DAG). Bayesian networks are ideal for taking an event that occurred and predicting the likelihood that anyone of several possible known causes was the contributing factor. For example, a Bayesian network could represent the probabilistic relationships between diseases and symptoms. Given symptoms, the network can be used to compute the probabilities of the presence of various diseases.

And fix What is Bayes' Theorem text size

- # What is [Bayes' Theorem](https://en.wikipedia.org/wiki/Bayes%27_theorem)
+ ## What is [Bayes' Theorem](https://en.wikipedia.org/wiki/Bayes%27_theorem)

Refactor actions and create tests

Refactor and add tests for the following actions at src/actions/index.js:

  • removeNode
  • addParent
  • removeParent

Theses three actions don't need nodes payload anymore so, you can remove it and then create the missing test for these actions.

- export const removeNode = id => (dispatch, getState) => {
+ export const removeNode = id => (dispatch) => {
  dispatch({
    type: REMOVE_NODE,
-    payload: { id, nodes: getNodes(getState()) }
+    payload: { id },
  });

  dispatch(persistState());
};

Create test for actions

Create tests for the following actions at action/index.js:

  • persistState
  • newNetwork
  • loadNetwork
  • changeNetworkProperty
  • addNode
  • changeNodeId
  • changeNodeDescription
  • changeNodePosition
  • setBelief
  • addSuperNode
  • removeSuperNode
  • addLinkage
  • removeLinkage

You don't need create a test for these actions: removeNode, addParent, removeParent, changeNodeStates. I will change them and then I will create the tests.

You can create more than one PR if you wanna so, the PR will not be too big ;)

Here is a test code example you can follow:

import { NETWORK_KINDS } from 'constants/network';
import {
  persistState,
  newNetwork,
  loadNetwork,
  PERSIST_STATE,
  NEW_NETWORK,
  LOAD_NETWORK,
} from './index';

const persistStateAction = {
  type: PERSIST_STATE,
};

describe('Actions', () => {
  let dispatch;

  beforeAll(() => {
    dispatch = jest.fn();
  });

  beforeEach(() => {
    dispatch.mockClear();
  });

  describe('persistState', () => {
    let actionResult;

    beforeEach(() => {
      actionResult = persistState();
    });

    it('calls dispatch with type PERSIST_STATE', () => {
      expect(actionResult).toEqual(persistStateAction);
    });
  });

  describe('newNetwork', () => {
    describe('When passing no kind parameter', () => {
      beforeEach(() => {
        newNetwork()(dispatch);
      });

      it('calls dispatch with type NEW_NETWORK and kind BN', () => {
        expect(dispatch).toHaveBeenNthCalledWith(1, {
          type: NEW_NETWORK,
          kind: NETWORK_KINDS.BN,
        });
      });

      it('calls dispatch persist action', () => {
        expect(dispatch).toHaveBeenNthCalledWith(2, persistStateAction);
      });
    });

    describe('When passing with kind parameter as MSBN', () => {
      beforeEach(() => {
        newNetwork(NETWORK_KINDS.MSBN)(dispatch);
      });

      it('calls dispatch with type NEW_NETWORK and kind BN', () => {
        expect(dispatch).toHaveBeenNthCalledWith(1, {
          type: NEW_NETWORK,
          kind: NETWORK_KINDS.MSBN,
        });
      });

      it('calls dispatch persist action', () => {
        expect(dispatch).toHaveBeenNthCalledWith(2, persistStateAction);
      });
    });
  });

  describe('loadNetwork', () => {
    const state = { key: 'value' };

    beforeEach(() => {
      loadNetwork(state)(dispatch);
    });

    it('calls dispatch with type LOAD_NETWORK and payload with state', () => {
      expect(dispatch).toHaveBeenNthCalledWith(1, {
        type: LOAD_NETWORK,
        payload: { state },
      });
    });

    it('calls dispatch persist action', () => {
      expect(dispatch).toHaveBeenNthCalledWith(2, persistStateAction);
    });
  });
});

Add test for subnetwork reducers

This issue can ben two PRs but, do the tests first to make sure nothing breaks.
Also, you can use ramda to refactor:

// current
case LOAD_NETWORK: {
  const { subnetworks } = action.payload.state.network;
  return subnetworks || [];
}
// with Ramda
import { path, defaultTo } from 'ramda';

const pathPayloadStateNetwork = path(['payload', 'state', 'network'])
const defaultToEmptyArray = defaultTo([]);

case LOAD_NETWORK: {
  return defaultToEmptyArray(pathPayloadStateNetwork(action))
}
// or
const onLoadNetwork = pipe(pathPayloadStateNetwork, defaultToEmptyArray);
case LOAD_NETWORK: {
  return onLoadNetwork(action)
}

Other details about this reducer:

  • There is a method called colorsNol that should be something like getRandomColor. I believe you can extract this to a new utils like /src/utils/colors or use a package for it. Just to be clear: a super-node is a sub-network that will receive a random color. Yes, I'm awere that super-node is a really bad name but I will change it in another PR.
  • There is a condition that I don't know if is valid: if (nodes && positions) {. This was made because we can receive two different networks: the current format and the old.

networks.zip

In this .zip you can find the two networks. You can move theses networks to a __fixtures__ folder and then use it in your tests.

  • first-version-network.json - the first/legacy version
  • simple-network.json - the current version

Well, thats it 😄

Add test for network reducers

This reducer is not 100% pure in some cases, the current state is being changed:

if (network.kind === undefined) network.kind = NETWORK_KINDS.BN;
if (network.id === undefined) network.id = v4();

Anyway, would be great use ramda to refactor this reducer and extract all the possible logic from there.

Set image in Bayes' theorem

Update README to:

- **P(A | B) = (P(B | A).P(A)) / P(B)**
+ ![Bayes' theorem formula](https://wikimedia.org/api/rest_v1/media/math/render/svg/87c061fe1c7430a5201eef3fa50f9d00eac78810)

And fix the link:

- * P(A | B) is a conditional probability(https://en.wikipedia.org/wiki/Conditional_probability): the likelihood of event A occurring given that B is true.
+ * P(A | B) is a [conditional probability](https://en.wikipedia.org/wiki/Conditional_probability): the likelihood of event A occurring given that B is true.

Alert when modifying states

When there're some modifications on some node's states, parents, children, etc... the software should warn the user that the CPTs must be checked.

Remove `CHANGE_NETWORK_PROPERTY` action

Currently, CHANGE_NETWORK_PROPERTY changes the following network's properties:

  • name
  • description
  • width
  • height
  • inferenceEnabled
  • propertiesPanelVisible

I think would be better to create a more explicit action, for example, instead of CHANGE_NETWORK_PROPERTY would be better to have a UPDATE_NETWORK_NAME.
Follow the name example, to accomplish this change you must:

Create the action

src/actions/network.js

export const onUpdateNetworkName = (name) => (dispatch) => {
  dispatch({
    type: UPDATE_NETWORK_NAME,
    payload: { name },
  });

  dispatch(persistState());
};

Remember to add this src/actions/network.js at src/actions/index.js.

Create the action test

Check src/actions/index.test.js example ;)

Update network's name reducer

src/reducers/network/name.js

- const updateProperty = updateNetworkProperty('name');
+ const getName = path(['payload', 'name']);

export default (state = defaultValue, action) => {
  switch (action.type) {
    case LOAD_NETWORK:
      return getNetworkName(action);
    case NEW_NETWORK:
      return defaultValue;
-    case CHANGE_NETWORK_PROPERTY:
-      return updateProperty(state, action);
+    case UPDATE_NETWORK_NAME:
+      return getName(action);
    default:
      return state;
  }
};

Update network's name reducer

src/reducers/network/name.test.js

describe('Network Name Reducer', () => {
  ...
-  describe('CHANGE_NETWORK_PROPERTY', () => {
-    describe('When property name is "name"', () => {
-      it('returns new value', () => {
-        expect(
-          reducer(
-            'old name',
-            {
-              type: CHANGE_NETWORK_PROPERTY,
-              payload: { name: 'name', value: 'new name' },
-            },
-          ),
-        ).toBe('new name');
-      });
-    });
-
-    describe('When property name is not "name"', () => {
-      it('returns current state value', () => {
-        expect(
-          reducer(
-            'old name',
-            {
-              type: CHANGE_NETWORK_PROPERTY,
-              payload: { name: 'other', value: 'new name' },
-            },
-          ),
-        ).toBe('old name');
-      });
-    });
-  });

+  describe('UPDATE_NETWORK_NAME', () => {
+    it('returns new value', () => {
+      expect(
+        reducer(
+          'old name',
+          {
+            type: CHANGE_NETWORK_PROPERTY,
+            payload: { name: 'name', value: 'new name' },
+          },
+        ),
+      ).toBe('new name');
+    });
+  });
});

Review validations

Until a MVP is released, I'm not giving too much attention to validations (duplicate ids, max length, required, etc.).

Fix lint errors

There are a lot of errors on lint:

Screen Shot 2019-03-18 at 11 11 10

I will create a PR to fix all this errors to keep this project up to date.

Show inference results as bars

Instead of showing numbers, show bars that have a width relative to its probability.

  • It should be optional (an option to toggle it)
  • It should have a tooltip to show the value

Update README

Add more details about bayes' theorem.

Update:

# BayesJS Editor

An editor for Bayesian Networks built in React the use bayes' theorem.

Add

# What is bayes' theorem

In probability theory and statistics, Bayes’ theorem describes the probability of an event, based on prior knowledge of conditions that might be related to the event. For example, if cancer is related to age, then, using Bayes’ theorem, a person's age can be used to more accurately assess the probability that they have cancer than can be done without knowledge of the person’s age.

Also, you can add links from Wikipedia like:

Add test e2e

Use Cypress to test essential things like:

  • Create a node
  • Edit CPTs
  • Add/Remove states
  • Open a network
  • Change node name
    ...

I'm already working on it.

Explore support IE

IE 11 doesn't support Array#find.

Add polyfill for it and explore if something else is broken.

Add why-did-you-update package

I think would be great to have why did you update running in DEV.

In this project, we already had the error of render unnecessary thing like, render all network again just because of one node change it's position. So, to make sure we don't have this kind of problem again this package can help us :)

Add test for linkages reducers

This issue can ben two PRs but, do the tests first to make sure nothing breaks.
Also, you can use ramda to refactor:

// current
case REMOVE_LINKAGE: {
  const { id } = action.payload;
  const newState = { ...state };
  delete newState[id];

  return newState;
}
// with Ramda
import { dissoc } from 'ramda';
... 
case REMOVE_LINKAGE: {
  const { id } = action.payload;

  return dissoc(id, state);
}

Confusing arrow direction

When there are multiple arrows coming from and to the same point, it's very confusing.

The arrows on the SPRINKLER node illustrates this.

image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.