As part of my activity in this role, I will be posting regular articles on Angular covering its use in science and engineering applications. This post serves as a perma-link to all the articles.

I hope you find something of use in them and best of luck in your Angular efforts!

– Angular Elements: So Easy, Even A Mathematician Could Do It (also covers Angular running inside React)

– Scientific Visualization With Angular (bonus link to single-quadrant graphing engine as an Angular Component)

– Editing Tabular Data In Angular (illustrates a lot of techniques I’ve used in actual applications)

]]>

Shortly after the Angular 2 release in 2016, I began organizing frequently used Typescript code and Angular components into a library project I named ‘The Angular Dev Toolkit.’ In reality, the library is largely Typescript libraries and utilities, with a small number of Angular components/directives/pipes/validators. I’ve never liked this organization, so the library will be refactored in the future to better adhere to separation of concerns. I have become a fan of the NWRL Nx utility, so the project will be re-scaffolded with Nx. Typescript libraries/utils and Angular-specific features will be in separate libraries, all under the ‘umbrella’ (too bad I can’t use the Umbrella Corp logo) of ‘The Angular Dev Toolkit.’

In the mean time, this post serves as a permanent link to describe the current library contents. All code in the toolkit is Apache 2.0 licensed and while it contains some of my open-source code, much of the library is closed and client-proprietary. As such, it’s one of my key competitive advantages, since every client who hires me is several tens of thousands of lines of code further ahead from day one than if they hired someone else. Every client may continue to use the library free of charge and for any commercial project for as long as desired, after my contract is over.

As of Dec 2018, the library has been segmented into Angular and Typescript-only folders with a *package.json* file that can be used to install Angular+Typescript or Typescript-only dependencies.

So, here we go (folders are bold highlighted).

ANGULAR DEV TOOLKITsrc-- adt-angular (Angular-specific functionality) --actions(simple set of ngrx/store actions for generic app and user slices of the store) --components----- breadcrumbs (simple breadcrumbs that can be extended for a variety of apps) ----- graph-component (single-quadrant graphing engine) ----- login (template for a basic login screen) ----- menu (template for a basic menu) ----- paging-bar (configurable paging bar) --directives----- basic-scene.directive.ts (for BabylonJS) ----- canvas-selector.directive.ts ----- href.directive.ts ----- input-selector.directive.ts ----- make-clickable.directive.ts ----- media-toggle.directive.ts --enums----- UserRoleEnum.ts --guards----- AuthGuard.ts --interceptors----- ErrorInterceptor.ts ----- TokenInterceptor.ts --logging----- Log.ts (general logging class to use with Angular services) ---models(some generic models for use with ngrx/store - slices of global store) ----- App.ts (application model) ----- IAddress.ts (interface for an address model) ----- IAppAction.ts (interface for an app action) ----- ILoggingAction.ts (interface for a logging action) ----- IUser.ts (interface for a User model) ----- User.ts (specific implementation of IUser) --pipes(some Angular pipes) ----- null-dash (display a dash for null/undefined value) ----- phoneNumber (format a phone number that is provided as a continuous set of digits) ----- scientific (display a number in scientific/exponential notation) --reducers(stock reducers for ngrx/store) ----- app.ts (baseline application model reducer) ----- user.ts (baseline user model reducer) --services----- breadcrumb.service.ts ----- media.services.ts ----- window-resize.service.ts --state(more models for ngrx/store) ----- State.ts (a baseline state consisting of app and user slices) --styles(some baseline styles for ADT components) ----- colors.scss ----- core.scss ----- text.scss --validators(form validators) ----- CCTypes.ts (types of credit cards) ----- CCValidator.ts (validate credit card number) ----- CreditCardValidator.ts (Angular form validator for credit card numbers) ----- Email validator (Angular email form validator) ----- PasswordStrength.ts (evaluate password strength on 1-100 basis) ----- PasswordValidator.ts (Angular form password validator - baseline function) ----- patternValidator.ts (Angular form regex pattern validation) -- adt-tslib (Typescript-only libraries) --core-----drawing-------- ADTRectangleSelector (Canvas press-drag selector for rectangular bounding-box) -------- DrawStack.ts (draw-stack for drawing an object inside an arbitrary bounding-box) -------- LineDecoratorFactory.ts (Factory to generate line decorators, i.e. dashed, dotted) -------- ShapeConstants.ts (constants that apply to imperatively drawing a variety of shapes) -----shapes-------- ArcTo.ts (implementation of the arc-to algorithm) -------- Arrow.ts (arrows in horizontal or vertical orientation) -------- CircularArc.ts (circular arc in [0, 2*pi]) -------- CSection.ts (C-section) -------- CurvedArrow.ts (arrow with a quad bezier base) -------- EllipticalArc.ts (elliptical arc in [0, 2*pi] -------- ISection.ts (I-section) -------- LSection.ts (L-section) -------- Wedge.ts (wedges or pie sections) --interfaces----- IBreadcrumbItem.ts ----- IHierarchySearchResult.ts (result from recursive, hierarchical search) ----- IMenuItem.ts ----- IPageChange.ts ----- IPoint.ts ----- IQuadBezierParam.ts (parameters of a quadratic Bezier curve) ----- ISimAnneal.ts (simulated annealing problem description, WIP) --libs-----ai--------decision-tree(data-driven decision tree based on general tree and expression engine) ----------- DecisionTree.ts --------fsm(reactive, data-driven finite-state machine) ----------- FiniteStateMachine.ts --------pathfinding(placeholder for A* for waypoints, WIP) ----------- AStarWaypoint.ts --------sequencing-engine(drive multiple decision trees in sequence, also data-driven) ----------- SequencingEngine.ts --------simanneal(simulated annealing solver, WIP) ---------- Simulate.ts --------text-vsm(text to Vector State Model, text freq., inv. doc. freq, cosine sim. library) ---------- cosineSim.ts ---------- score.ts ---------- textToVSM.ts ---------- TlIDF.ts ---------- toVector.ts ---------- vsmMod.ts ---------- vsmNorm.ts -----barcode-------- code39.ts (code-39 barcode generator for Canvas surface) -----composable(functions that can be used in composition) -------- Fcns.ts ---------- reverseChars ---------- initials ---------- padLeft ---------- minValue ---------- maxValue ---------- areGreaterThan ---------- getAllGreaterThan ---------- indexFirstGreaterThan -----misc-------- CookieMgr.ts -------- DayHourOffset.ts -------- DayIterator.ts -------- getIntervalsBetweenDays.ts -------- LocalStorageMgr.ts -------- orderedPermutations (Knuth's algorithm-L for an array of Objects) -------- toCap.ts -------- toMsec.ts -----paging-------- Paging.ts (manage paging of an array of Object data) -----search-------- Hierarchy.ts (recursively search a hierarchical structure) -----sorting-------- CountingSort.ts (implementation of counting sort algorithm) -------- SortOn (sort an array of objects based on a set of input properties) -----tsmt(subset of the Typescript Math Toolkit) -------- crypto ----------- core -------------- Euclid.ts (LCM, GCD, and extended Euclid's algorithm) --------data-frame(Dataframe analysis, similar to R) ----------- Chi2.ts (chi-squared - to be merged into statistics folder) ----------- CompareUtils.ts (scalar/vector comparisons) ----------- DataFrame.ts (organize/analyze 2D tables of string/numeric data) ----------- FrameStats.ts (variety of stats on data frame columns) ----------- Spcfcn.ts (special functions, beta, approx beta, beta-inverse, log-gamma, etc - to be placed in math or statistics) ----------- TableAnalysis.ts (one-way and two-way table analysis - cross-tab) --------datastructures----------- AVLTree.ts ----------- BTreeLight.ts ----------- BTreeNode.ts ----------- BTreeUtils.ts ----------- DisjointSet.ts ----------- DSNode.ts ----------- DSNodeImpl.ts ----------- Graph.ts ----------- GraphArc.ts ----------- Heap.ts ----------- IBTree.ts ----------- IPrioritizable ----------- LinkedList.ts ----------- ListNode.ts ----------- Prioritizable.ts ----------- PriorityQueue.ts ----------- Queue.ts ----------- SimpleGraph.ts ----------- SkipList.ts ----------- SkipListNode.ts ----------- SkipListNodeImpl.ts ----------- Stack.ts ----------- Tree.ts ----------- TreeNode.ts --------decorators(line decorators for programmatic drawing) ----------- AbstractLineDecorator.ts ----------- DashedLineDecorator.ts ----------- DottedLineDecorator.ts ----------- IGraphics.ts ----------- ILineDecorator.ts --------drawing(utilities for programmatic drawing/graphing) ----------- Axis.ts ----------- FunctionDrawProperties.ts ----------- GraphAxis.ts ----------- GraphDrawProperties.ts ----------- LabelDrawProperties.ts ----------- Quadrant.ts --------expression-engine(parse and evaluate expressions for multiple independent variables) ----------- ExpressionEngine.ts --------functionparser(parse and evaluate functions of one or more independent variables) ----------- FunctionParser.ts --------geom(analytic and computational geometry utilities; line simplification methods) ----------- Circle.ts ----------- ConvexHull.ts ----------- Coordinate.ts ----------- DouglasPeucker.ts ----------- IPlanarCurve.ts ----------- Lang.js ----------- Layout.ts ----------- LayoutObject.ts ----------- LayoutPoint.ts ----------- LayoutTypeEnum.ts ----------- Line.ts ----------- McMaster.ts ----------- PlanarCurve.ts ----------- Point.ts ----------- Polygon.ts ----------- PolyLayout.ts ----------- PolyLayoutObject.ts ----------- QuadBezier.ts ----------- RegularNGon.ts ----------- TwoPointCurve.ts ----------- Units.ts --------item-allocation(continuous and 0-1 knapsack problem) ----------- AllocatableItem.ts ----------- DefaultItemStrategy.ts ----------- IAllocatableItemProps.ts ----------- IAllocationStrategy.ts ----------- ItemAllocation.ts ----------- Knapsack.ts ----------- Matrix.ts --------math(some general math utils) ----------- Derivative.ts (some methods for numerical estimation of a function's first derivative) ----------- Matrix.ts (general dense matrix and linear system solver) ----------- Neville.ts (Neville's method for polynomial interpolation) ----------- PrimeFactorization.ts (prime factorization of modest-sized integers) ----------- Quaternion.ts (general quaternion operations and slerp) ----------- RepeatToFrac.ts (repeating decimal to fraction) --------misc----------- UndoRedo.ts (supports general Undo/Redo operations - built on top of TSMT Linked List) --------random(utilities for pseudo-random generation and random integer in a range) ----------- RandomIntInRange.ts (with endpoint bias compensation) ----------- SeededRng.ts (implementation of Park-Miller generator) --------shuffle----------- fisher-yates.ts (fisher-rates algorithm ... don't leave home without it) --------splines----------- CubicSpline.ts (natural cubic spline) --------statistics(lies, damned lies, and statistics) ----------- bayes -------------- Bayes.ts -------------- FrequencyTable.ts ----------- BinomialCoef.ts ----------- DataStats.ts ----------- ExtNormal.ts (extended Normal class - approx. distribution with sequence of quad bezier curves) ----------- Normal.ts (computations dealing with normal distribution) ----------- Poisson.ts (computations dealing with Poisson distribution) ----------- regression -------------- Bagging.ts (helper class for computations involving bagging) -------------- Bllsq.ts (bagged linear least squares) -------------- Deviates.ts (pseudo-random deviates from popular distributions) -------------- llsq.ts (linear least squares) -------------- Pllsq.ts (general polynomial least squares) -----------utils(general utilities) -------------- ApproxEqual.ts (compare numbers for approximate equality) -------------- ArrayFunctions.ts (some one-liner array functions) -------------- balancedparens.ts (balanced parens, brackets, etc) -------------- Bisect.ts (bisect and interval, looking for a root) -------------- CircleUtils.ts (a large number of circle-related functions) -------------- CompareUtils.ts (comparisons for exact/approximate equality) -------------- CubicRoots.ts (root-finder for cubic polynomials with real coefficients) -------------- Gauss.ts (Gaussian quadrature for numerical integration) -------------- General.ts (replacements - to be inlined - for common loadash functions) -------------- GeomUtils.ts (large collection of general analytic geometry functions) -------------- Halley.ts (Halley's method for root finding) -------------- Interp.ts (linear interpolation) -------------- LayoutUtils.ts (utilities to support a layout - mapping between user-coordinates and canvas pixel space) -------------- Limits.ts (some machine limits useful in numerical algorithms) -------------- MathUtils.ts (another place to find LCM, GCD and simple arithmetic-related utilities) -------------- Newton.ts (Newton's method for root-finding) -------------- NumberFormatter.ts (general number formatter - supports exponentiation and scientific notation) -------------- PackingUtils.ts (some utilities for spatial packing problems) -------------- PointUtils.ts (utilities involving computations on Points and Point clouds) -------------- QuadBezierInterpolate.ts (three-point quadratic bezier interpolation) -------------- Solve2x2.ts (linear system solver optimized for 2x2 matrices) -------------- StringUtils.ts (a wide variety of string utility functions) -------------- TableFilter.ts (filter a selection of table rows (and optional 2nd table) based on supplied column values) -------------- Timer.ts (reactive, general-purpose timing function) -------------- TWBRF.ts (port of Jack Crenshaw's Worlds Best Root Finder) ------------validation--------------- Validation.ts (number/vector/matrix checks for validity and basic comparison vs a tolerance)

If you have any questions about the library or wish to schedule a consultation on your project, please contact me at theAlgorithmist [at] gmail [dot] com.

Thanks and refer back to this link frequently to keep up on changes to the toolkit.

]]>And, since I know you want to get your hands on some code, point your friendly neighborhood browser to this link and enjoy!

]]>

I recently received an inquiry regarding a problem to be included in the Typescript Programming Test Problems repo. This one requires a more extensive writeup for the analysis, so I decided to devote a blog post to the topic.

Problem Statement: You are given a function, *die5()* that simulates the roll of a five-sided die. Write a function that uses only *die5()* to simulate the roll of a die with a small prime number of faces. Discuss limitations or edge cases involved with the use of this function.

At first read, my guess is that the prime number of faces has nothing to do with the problem. It’s indicative of real-world applications where a person is often given either too little or too much information to solve a problem. One of the challenges is knowing what needs to be obtained for a solution and what can be discarded. So, let’s restate the problem slightly as how to simulate the roll of a k-sided die (where k is an integer > 5) using only a five-sided die.

A five-sided die can only generate five combinations, 1, 2, 3, 4, and 5. For a *k*-sided die, we need to be able to uniformly generate *k* combinations, but are restricted to a five-sided die as a generator. There was, however, no restriction placed on the number of rolls of the five-sided die. Let’s say we roll the five-side die twice. There are 25 possible combinations. Three rolls has 125 possible combinations. If r is a positive integer > 0, then *r* rolls generates *5^r* possible combinations.

I like to have some sort of mathematical model for such problems. Let [1,5] refer to a pseudo-random selection of integers in the interval one to five, inclusive. We expect *die5()* to approximately model uniform samples from [1,5]. The statistical quality of the final result, of course, depends on the quality of that sampling.

With 25 combinations, we could theoretically model the roll of up to a 25-sided die. For numbers between 6 and 25, modulo arithmetic can help by dividing 25 in to ‘bins’ of length *k*, with potential leftover. Consider the next two primes in sequence, 7 and 11. Here are the ‘bins’ for each case with the corresponding roll of the 7-sided die.

7-sided die with 25 combinations (3 full bins, one partial)

1 2 3 4 5 6 7 - 8 9 10 11 12 13 14 - 15 16 17 18 19 20 21 - 22 23 24 25 1 2 3 4 5 6 7 - 1 2 3 4 5 6 7 - 1 2 3 4 5 6 7 - * * * *

11-sided die with 25 combinations (two full bins, on partial)

1 2 3 4 5 6 7 8 9 10 11 - 12 13 14 15 16 17 18 19 20 21 22 - 23 34 25 1 2 3 4 5 6 7 8 9 10 11 - 1 2 3 4 5 6 7 8 9 10 11 - * * *

Consider a generating function, *g(r)*, that depends only on the number of rolls, *r*, of a five-sided die. For the above examples, *r = 2*. The generating function should return uniform samples of *[1,5^r] or [1,25]* for the two-roll case. If a generated number falls in a ‘full’ bin, then take the result mod *k* and add one. That generates a sample in* [1,k]*, which is the desired result.

A decision must be made on how to handle cases where the generating function produces a number in a ‘partial’ bin. The easiest solution is to simply call the generator recursively. This has the benefit of being simple, and recursive programming is a fad for testers, so you are likely to gain some style points :). It does have the side-effect of ’throwing away’ iterates in [1,5]. For a perfectly uniform generation in [1,5], that is not an issue. However, in practice, *die5()* is likely to suffer from statistical flaws that could have a downstream effect on the final result.

So, what does a generating function look like? A simple, linear polynomial is as good a start as any, so consider models of the following form for the two-roll generator

*g(2) = a[1,5] + b[1,5] – c*

where a, b, and c are positive integers. This generalizes in the multi-roll case to

*g(r) = a[1,5] + b(1)[1,5] + b(2)[1,5] + … + b(r-1)[1,5] – c*

Returning to the two-roll generator, what constraints can be placed on the generation? Define *n* to be the largest integer such that* nk <= 25*. This is the number of full bins, given the number of sides of the die, *k*. The smallest input roll is one and the largest is five. When a one is rolled both times, the result should be equal to one to cover the low range of [1,25]. When a five is rolled both times, the generator should produce 25 in order to cover the high range of [1,25].

*1 <= a[1,5] + b[1,5] – c <= 25*

At the high end of both [1,5] rolls, the generated value should be greater than or equal to *nk*.

This produces three unknowns, *a*, *b*, and *c*, but only two constraints. So, there is a family of possible generating functions.

The generator must also return uniform numbers in [1,25]. If a frequency count of each generated number is computed, the histogram should be perfectly flat as the number of generations tends to infinity. A way to test for this is to loop over the generations for each possible roll of the five-sided die and simply print out the result. Numbers that are skipped or repeated lead to bias in *g(r)*.

To resolve the underdetermined problem, set *b = 1* and use the two constraints to solve for *a* and *c*. Since addition is associative, the model is the same as setting* a = 1* and solving for *b* and *c*. Just reverse the order of the *a* and *b* terms.

The simplified model is

*g(2) = a[1,5] + [1,5] – c*

Use the two extreme cases where *[1,5] = 1* and *[1,5] = 5* for both rolls fo the five-side die. At the low end,

*1 = a*1 + 1 – c = 1(a + 1) – c*

At the high end,

*25 = a*5 + 5 – c = 5(a + 1) – c*

Let *j = a + 1*, then

*1 = j – c or j = 1 + c*

*25 = 5j – c = 5(1+c) – c = 5 + 4c*

This yields *c = 20/4 = 5* and* j = 1 + c = 6*, which means* a = 5*.

Computation of *n* given *k* is pretty simple and the entire model is created in the *getCoefs()* function in the code distribution for this problem. It only works for the two-roll problem. I do not like the idea of generalizing this particular family to a number of rolls greater than two as it puts too much weight on the first roll. The likelihood of generating a model that is non-uniform is too great.

I solved the three-roll model by hand and got *a = 29* and *c = 30*. I did verify that the two-roll model is uniform. I believe that the three-roll model is **not** uniform, but perhaps an astute reader will prove me wrong (I have very limited time to devote to these problems). So, I can address the two-roll model which is good for a ‘small’ number of primes as long as *k <= 25* and we have a ‘perfect’ quality generator for [1,5]. In practice, the latter will not be true.

To simulate the roll of a *k*-sided die using a generator for [1,5], pass in the two-roll model and generate two rolls in [1,5]. Use those results in the generating function for *k*. Then, test if the value is less than or equal to *n*k. If so, modulo-*k* math produces a result. Otherwise, call the generating function again. This code is provided in the simulate() function,

let roll: number = model.a*RandomIntInRange.generateInRange(1,5) + RandomIntInRange.generateInRange(1,5) - model.c; // test if number is within the integer multiple of k if (roll <= model.n*k) { // return result mod k plus 1 to compensate for 1-based index return (roll%k) + 1; } else { // too bad ... try again return simulateRoll(model, k); }

where the Typescript Math Toolkit *RandomIntInRange* class is used to generate iterates in [1,5]. Since it is based on the system-supplied *Math.random()*, it is a typical linear congruential generator and suffers from all the known flaws of such generators.

I supplied a *validate()* function that computes the frequency histogram for a given value of k. This is used in the specs for *k = 6, 7, 8, and 9*. The numbers look pretty good, but would be better for a higher-quality [1,5] generator. Try it for other values of *k* and a greater number of iterations per validation and study the results.

So, the two limitations are we can only simulate up to a 25-sided die using two rolls of a five-sided die and the final output is dependent on the statistical quality of the generator that simulates the five-sided die. The general problem (for any value of *k > 5*) requires extensive iteration and simulation to produce a model that meets the necessary constraints, particularly that of uniform distribution in [1, 5^r].

Here is the repo – experiment with the code and have fun!

]]>This project has a good writeup on the Github page and I know you want to go directly to the code, so point your friendly neighborhood browser in this direction.

https://github.com/theAlgorithmist/Angular5-Deeplearn

and don’t forget the three rules,

1 – Experiment

2 – Have Fun

3 – Drink Coffee

Rule #3 is the most important.

]]>This a follow-on to the developments in my Angular Dev Toolkit regarding data-driven application logic. When discussing the expression-based decision tree, I mentioned that it would be possible to have a leaf node contain the name and initial state of a finite-state machine. For that approach, the FSM must be data-driven and to fit nicely into the general Angular idiom, I also wanted a machine that was reactive.

The Decision Tree and Sequencing Engine are general-purpose derivatives of work previously implemented for NDA clients (I had to rewrite everything pretty much from memory). As such, they will remain in the client-only Angular Dev Toolkit. Subsequent private and online discussions led me to believe that the Finite State Machine was of suitable general interest to open-source the baseline version.

If you are interested in background, then here is one of my favorite online descriptions of a Finite State Machine and is closest to how I learned the topic ‘back in the day.’

The machine implementation in this repo is Mealy-style, although a state may be passed as data, so Moore-style machines can be used within this framework.

A FSM consists of a number of named states, and transitions from those states. One or more states may be defined as an *acceptance* state, meaning that after exercising the machine with a finite number of inputs, the machine may indicate acceptance of a certain criteria. This could be a valid string or that the number of zeros in a binary sequence is even. It is possible (and I’ve done this in C++) to define a *rejection* state with the convention that the machine may never transition out of this state. This is a way to catch and flag incorrect or other inputs.

The set of inputs to a FSM is called an *alphabet*.

The *FiniteStateMachine* class in this distribution has two possibly differentiating features from other implementations (particularly in Java, JS, and TS). One is that listening for specific state transitions is not performed by callback functions applied to those states. Instead, the machine is reactive. One or more (RxJs) *Observers* may subscribe to the machine and receive updates on all transitions that includes *from* and *to* states as well as relevant data.

**API**

The FiniteStateMachine class exposes the following models

// this is normally part of the Decision Tree library, but has been ripped out to make this distribution standalone export interface IDecisionTreeAction { success: boolean; // true if operation was successful node?: Object; // reference to the data Object in which an error was detected action: string; // action to take } /** * A state transition must have a 'from' and 'to' (named) state and may contain optional Object data */ export interface IStateTransition { from: string; to: string; data?: Object; } /** * Output from a state transition is the 'to' state and optional data obtained from the transition function */ export interface IStateOutput { to: string; data?: any; } /** * The transition function is Mealy-style, that is a transition to a new state is based on prior state and * input data. Since state is optional in this interface, pass the state name as data and a Moore-style * machine can be implemented. */ export interface transFunction { (data: any, state?: string): IStateOutput; }

The public API of the *FiniteStateMachine* class is as follows.

public static create(data: Object, name?: string): FiniteStateMachine | null public get numStates(): number public get numTransitions(): number public get currentState(): string public get states(): IterableIterator public get initialState(): string public get initialData(): Object | null public get isAcceptance(): boolean public get alphabet(): Array | null public fromJson(data: Object): IDecisionTreeAction public addState(stateName: string, acceptance: boolean=false): void public addTransition(from: string, to: transFunction): boolean public addSubscriber(observer: Observer ): void public next(input: any, initialState?: string): IStateOutput | null public clear(): void

**Usage**

The FSM in this distribution may be used by directly assigning states and transition functions. The machine may also be described with *Object* data. These cases are best illustrated by example.

First, consider one of the machines discussed in the above introductions to FSM’s. This machine is designed to test sequences consisting of the letters a, b, c, and d, which is the machine’s alphabet.

The states are S1, S2, S3, and S4, the latter of which is the acceptance state. The machine’s initial state is S1 and letters are input one at a time. The machine is tested for being in the acceptance state after the final transition (refer to the above link for the state diagram).

This machine can be implemented with the following code (which is provided in the specs located in the test folder) which presumes a string input Array, str, i.e.

const str: Array = [‘a’, ‘a’, ‘a’, ‘a’, ‘c’, ‘d’];

Note that the transition functions are defined statically in code, which allows them to use stronger typing than is described in the transFunction interface.

For compactness, testing the return value from adding a transition is not included in the example.

const f12: transFunction = (data: string) => { return data == 'a' ? {to: 'S2'} : {to: 'S1'} }; const f22: transFunction = (data: string) => { return data == 'a' ? {to: 'S2'} : (data == 'b' ? {to: 'S1'} : (data == 'c' ? {to: 'S4'} : {to: 'S2'})) }; const f32: transFunction = (data: string) => { return data == 'a' ? {to: 'S1'} : (data == 'b' ? {to: 'S4'} : {to: 'S3'}) }; const f42: transFunction = (data: string) => { return data == 'd' ? {to: 'S3'} : {to: 'S4'} }; . . . const __machine: FiniteStateMachine = new FiniteStateMachine(); __machine.addState('S1'); __machine.addState('S2'); __machine.addState('S3'); __machine.addState('S4', true); __machine.addTransition('S1', f12); __machine.addTransition('S2', f22); __machine.addTransition('S3', f32); __machine.addTransition('S4', f42); const n: number = str.length; let i: number; let state: IStateOutput = __machine.next(str[0], 'S1'); // set the initial state and first input for (i = 1; i < n; ++i) { state = __machine.next(str[i]); }

Based on the machine’s construction, we would expect *__machine.isAcceptance* to be false. This is also the answer to the quiz question posed on the site from which the example was taken

Many algorithms can be expressed as a FSM and Regex is equivalent to FSM (Kleene’s Theorem), for example. Whether or not one should implement an algorithm using a FSM is another topic. One example that often does not seem to fit a FSM architecture is the ‘change machine’ problem (this was a lab exercise in college).

Consider a machine that accepts coins (penny, nickel, dime, quarter) for an amount less than a dollar. After each coin is deposited, the machine updates the remaining balance, indicates if sufficient payment has been made, and computes any necessary change.

Alphabet and machine states may be considered equivalent in this machine, so we define states p, n, d, q, and c for the coin denominations and a ‘complete’ state. There is no transition out of the complete state, which is also the acceptance state for this machine.

Code to implement this machine is provided in the specs.

The most potentially useful feature of this FSM is the ability to describe a machine in data. This allows multiple machines to be defined in metadata and then dynamically applied based on real-time conditions in an application.

The string-test machine shown above can be described in the following data *Object*

const machine1: Object = { name: 'StringTest', initialState: "S1", alphabet: [ 'a', 'b', 'c', 'd' ], states: [ { name: 'S1', isAcceptance: false, transition: "return data == 'a' ? {to: 'S2'} : {to: 'S1'}" }, { name: 'S2', isAcceptance: false, transition: "return data == 'a' ? {to: 'S2'} : (data == 'b' ? {to: 'S1'} : (data == 'c' ? {to: 'S4'} : {to: 'S2'}))" }, { name: 'S3', isAcceptance: false, transition: "return data == 'a' ? {to: 'S1'} : (data == 'b' ? {to: 'S4'} : {to: 'S3'})" }, { name: 'S4', isAcceptance: true, transition: "return data == 'd' ? {to: 'S3'} : {to: 'S4'}" }, ] };

Transition functions defined in data will be called with arguments that strictly match the transFunction interface. They must adhere to the following conventions.

- Only the function body need be defined in a string.
- The variable names data and state are arguments. Use them as such.
- Transition functions must be pure.
- The self-referential pointer (this) is bound to the global context due to
*Function*constructor invocation. Do not use*this*inside the function body. - Do not reference any variables that are not defined in the function body as we can not be ‘loose’ regarding execution context of these functions.

In a typical application, the above Object description is input external to the application. Implementation of the machine reduces to

let result: IDecisionTreeAction = __machine.fromJson(machine1); let str: Array = ['a', 'b', 'a', 'c', 'd', 'a', 'a', 'c']; let n: number = str.length; let i: number; let state: IStateOutput; for (i = 0; i < n; ++i) { state = __machine.next(str[i]); }

We would expect _*_machine.isAcceptance* to be true.

Additional examples are provided in the specs that accompany the Github repo.

]]>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.

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.

]]>I’ve worked on two projects for NDA clients in the recent past in which it was necessary to clean up architecture and programming issues created by another developer who ‘left the project.’ Both cases had two aspects in common. The first was a myriad of problems created by stateful components and shared mutable state. These can be alleviated by refactoring the application to use a Redux-style architecture (I prefer @ngrx/store).

The second issue was more subtle and involved a complex nest of if-then-else-else-if-else blocks that were in the process of going as far as four levels deep at the time I was introduced to the project. These are difficult to debug (and test) and difficult for another developer to follow. This increases the complexity of both initial development and downstream maintenance. Interestingly, the actions that resulted from the rat’s nest of logic were quite simple; one was imperative routing (the final action was the route name and params). The second set of actions pertained to which components inside a particular page were made visible to the user (the action was to set a number of booleans that controlled *ngIf directives).

I was able to replace this logic with simple JSON and a decision tree in which each node contains an expression that evaluates to a boolean. While I no longer have access to these specific codes, the two low-level constituents to the solution are already in my Typescript Math Toolkit (an expression engine and a general Tree class). I recently sat down and created a *Decision Tree* for the Angular Dev Toolkit that provides a more production-worthy and reusable implementation of the above approach.

Let’s look at a trivial example. Suppose we have a block of logic that depends on a single, independent variable, *x*. An action is taken based on whether or not x is less than, equal to, or greater than zero. Of course, this is rather trivial to code, but it can be represented as the following decision tree.

The tree may be represented in the following format:

{ id: '0', priority: 1, expression: '', 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: '', action: 'A2'} ] }, { id: '3', priority: 3, expression: 'x < 5', children: [ {id: '6', priority: 3, expression: '', action: 'A3'} ] } ] }

This seems like a lot of work to describe something that is a fundamentally simple code block, but the power of the approach is evident as the logic increases in complexity or the logic needs to change frequently, or alter during interaction with the user.

Note that the root node has no expression to evaluate and this is an indication that the tree is to be fully evaluated every time. Consider a change request that adds the following conditions:

1 – The decision process should only be fully evaluated if the variable, *x*, is in the interval [lower, upper] where ‘lower’ and ‘upper’ are two new independent variables.

2 – For the middle path, x = 0, introduce a new variable, *y*. Take action ‘A2-1′ if y is less than zero and ‘A2-2′ if *y* is greater than zero. This requirement may be visualized as the tree,

and represented in data as,

{ 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: '6', priority: 3, expression: 'y > 0', children: [ {id: '9', priority: 1, expression: '', action: 'A2-2'} ] } ] }, { id: '3', priority: 3, expression: 'x > 0', children: [ {id: '7', priority: 3, expression: '', action: 'A3'} ] } ] }

The expression on the root node is called a ‘guard expression.’ It must pass during the first step of evaluation in order for the remainder of the tree to be processed. In Angular parlance, it plays a similar role to a route guard.

Actions defined at leaf nodes are simply strings that serve either as raw data or symbolic codes for some other action to be taken. They could be anything from the name of a route to a symbolic code to lookup in a function table to determine which function to execute. Or, as will be important in the sequel, they could be the name of another tree to evaluate.

Again, for small examples with almost no likelihood of change, this approach is over-engineering. The value of the decision tree is realized as logic grows in complexity and/or value is gained from expressing decisions in a data format that can be server-generated, changed dynamically, or one of many trees to evaluate may be chosen imperatively at runtime.

Node expressions may be any expression allowed by the Typescript Math Toolkit expression engine. Independent variables are extracted by the Decision Tree engine dynamically as each node is processed. These variables are assigned to the expression engine and then the expression is evaluated. Although variable values may be of type *string*, *number*, or *boolean*, the expression must evaluate to a *boolean*. If a node’s expression evaluates to true, then the node’s children are processed in the order in which they are defined. Child nodes are presumed ordered by decreasing priority, left-to-right. Tree processing continues until a leaf node is encountered with a named action, and this action serves as the return of an evaluation.

Here is a screen shot of some of the code from the internal *__evaluateTree()* method. Click on the image to see the full-size version.

Usage of the Decision Tree is relatively straightforward. The tree is created and then initialized from an data object,

const tree: DecisionTree = new DecisionTree(); const result: IDecisionTreeAction = tree.fromJson( {...} );

The *result* variable contains a success code indicating whether or not parsing of the tree structure was successful. In the case of failure, the offending node is available in the return to aid in debugging.

Values of the independent variables are taken from another data object, where the *Object* must have the variable names as keys. Corresponding values are used as the actual values of the independent variables from which an expression is evaluated, i.e.

let action: IDecisionTreeAction = tree.evaluate({lower: -10, upper: 10, x: -20, y: 4});

In most applications, such *Objects* already exist as part of the application’s state.

Work is also finished on the Sequencing Engine, which manages a Map of Decision Trees. I will also add a data-driven Finite State Machine as the output of a tree evaluation may be considered as a starting state in such as machine.

I’m rather excited about this suite of tools as it opens up a wide variety of possibilities for complex, interactive applications. And, as always, since the code is developed in Typescript, it may be used on BOTH the front- and back-end with Node/Express.

I hope you are equally excited and if you wish to contract with me (which means you automatically obtain a copy of the Angular Dev Toolkit), please contact me at theAlgorithmist [at] gmail [dot] com.

Thank you!

]]>