Angular Dev Toolkit Sequencing Engine

TSMT

This is a follow-up to my post about Decision Trees and the process of replacing complex (and static) logic in programs with a data-driven methodology.  A single decision tree can be a valuable tool, but as application complexity expands, so does the breadth and height of an individual tree.  As the tree expands in either dimension, its ability to be re-used diminishes.  The purpose of the Angular Dev Toolkit Sequencing Engine is to manage a Map of named decision trees.  The entire tree collection may be defined by a single block of JSON (with an automatic tree-naming convention) or individual trees may be assigned to the collection with independent blocks of data.

Trees are evaluated based on an input data Object, whose property names correspond to independent variables in the nodes of each decision tree.  Evaluation is in-order or by-name.  An in-order evaluation begins at the first decision tree in the collection and proceeds in order of tree definition until the evaluation is resolved.  A by-name evaluation begins at the named tree passed to the evaluate() method.  In this path, it is acceptable for a successful tree evaluation to be an action that is a name of another tree to evaluate.  If this is encountered, evaluation proceeds to the next named tree, and so on, until the evaluation is fully resolved.

This convention allows individual decision trees to remain smaller and more re-usable while allowing even more complex logic to be handled vs. that which is easily placed into a single tree.

As an example, consider the following data for a single decision tree with independent variables, x and y, upper, and lower.

{
  id: '0', priority: 1, expression: '(x >= lower) && (x <= upper)',
  children: [
  {
    id: '1', priority: 1, expression: 'x < 0',
    children: [
      {id: '4', priority: 1, expression: '', action: 'A1'}
    ]
  },
  {
    id: '2', priority: 2, expression: 'x = 0',
    children: [
      {
        id: '5', priority: 2, expression: 'y < 0',         
        children: [           
          {id: '8', priority: 1, expression: '', action: 'A2-1'}         
        ]       
      },       
      {         
        id: '5a', priority: 3, expression: 'y = 0',         
        children: [           
          {id: '9', priority: 1, expression: '', action: 'A2-2'}         
        ]       
      },       
      {         
        id: '6', priority: 4, expression: 'y > 0',
        children: [
          {id: '9', priority: 1, expression: '', action: 'A2-3'}
        ]
      }
    ]
  },
  {
    id: '3', priority: 3, expression: 'x > 0',
    children: [
      {id: '7', priority: 3, expression: '', action: 'A3'}
    ]
  }
]};

Now, suppose a change request is made that some additional logic must be evaluated if the y = 0 node is encountered. It is possible to add branches from that node, but the original tree structure could not be easily re-used in any other application. Instead, consider replacing this node,

{
  id: '5a', priority: 3, expression: 'y = 0',
  children: [
    {id: '9', priority: 1, expression: '', action: 'A2-2'}
  ]
}

with this one

{
  id: '5a', priority: 3, expression: 'y = 0',
  children: [
    {id: '9', priority: 1, expression: '', action: 'tree2'}
  ]
}

where ‘tree2′ is the name of a decision tree defined with the following data,

{
  id: '0', priority: 1, expression: '',
  children: [
    {
      id: '1', priority: 1, expression: '(z < 0) && (z > lower)',
      children: [
        {id: '4', priority: 1, expression: '', action: 'F1'}
      ]
    },
    {
      id: '2', priority: 2, expression: 'z = 0',
      children: [
        {id: '5', priority: 2, expression: '', action: 'F2'}
      ]
    },
    {
      id: '3', priority: 3, expression: '(z > 0) && (z < upper)',
      children: [
        {id: '6', priority: 3, expression: '', action: 'F3'}
      ]
    }
  ]
}

If these two trees are placed into the SequencingEngine with names ‘tree1′ and ‘tree2′, a by-name evaluation starting at ‘tree1′ is a simple, one-liner,

action = sequencer.evaluate({x: 0, y: 0, z: 0, lower: -10, upper: 10}, 
SequencingEngine.BY_NAME, 'tree1');

and this call results in the named action, ‘F2′, which can be acted upon inside the application.

Here is a screen shot of some of the evaluate() method.   Click on the image for a full-size view.

seq-engine

And, at the risk of some repetition, I will point out that the Angular Dev Toolkit is installed for all my contract clients. This is just one part of my overall value proposition.

The next development in this process is a reactive, data-driven, finite-state machine.

Comments are closed.