Friday, January 28, 2011

Traffic Flow Optimization

New research conducted by scientists at the Santa Fe Institute suggests that vehicle traffic flow through city street intersections improves simply by coordinating neighboring traffic lights. 

In a working paper published late last year, scientists found evidence of large-scale, city-wide traffic flow increases given dynamic adjustments at the local level, between neighboring lights. The method proposed by the research shows an overall reduction in mean travel times throughout a city. The research indicates that when traffic lights are allowed to self-organize, an overall system smoothness emerges.

One of the sample scenarios we use in our documentation for the rules engine is based on this research, which I think is fascinating. Our scenario combines multiple devices, cameras and traffic lights, and shows how they can be integrated using rules-based logic. I wanted to see if we could implement the method described in the paper using our little rules engine and the Ctrl (Content Type Rule Language). As it turns out, we can. 

Background

Traffic lights at city street intersections are usually controlled by timers that are scheduled to change light phases based on typical traffic conditions for the time of day. In many cases, traffic lights are timed to change more frequently during rush hours to minimize congestion and to allow traffic to flow in all directions at short intervals. 

Timed traffic lights provide for optimal flow as long as the traffic conditions the timers are based on, occur as expected. When this happens, drivers don't need to wait very long at a red light and if they're lucky, may ride out a wave of green lights. 

The Santa Fe Institute research shows that there are situations in which the simple clockwork mechanism that controls traffic lights does not always provide optimal flow. When there is an accident, for example, or when less traffic than what is expected comes through, then the timers are unable to adjust to changing conditions. The variability in the number of cars and individual driver speeds during the day makes it nearly impossible to optimize traffic flow using fixed timers. 

The Traffic Gap Scenario

The example we use in our documentation is focused on the detection of a traffic gap. We show that rules-based logic is triggered by events generated from video analytics that detect instances of traffic gaps. The emergence of a traffic gap behind a platoon of cars is a perfect opportunity to fine tune the lights. The basic principle underlying this kind of traffic optimization is simple: if there's no traffic, and a light is green, then change it and allow traffic to flow in another direction. 

The goal is to decide when to the switch lights and to self-adjust based on current traffic conditions. These conditions can be setup and detected by if-then logic defined in a rule. 

The Details

The scenario involves two street intersections:



A. First Street and Main Street

B. Second Street and Main Street

Traffic moves northbound on Main through First Street and Second Street. Traffic on First St. and Second St. moves East to West. This example requires one camera (Camera A) to be mounted on the traffic light that regulates traffic moving north on Main and First Street, as well as a second camera (Camera B) to be mounted on the traffic light that regulates traffic moving north on Main and Second Street. The video analytics for Camera A and B are setup to send an event to our rules engine when Directional Motion is detected as a car drives north through intersections A and B.



Since traffic gap detection is based on the absence of a Directional Motion event for a period of time through intersection A, the rule in this scenario says: "IF there is Directional Motion north through intersection B, AND there has not been Directional Motion north through intersection A for 20 seconds, THEN switch the traffic light and allow traffic to flow in another direction."

In the Ctrl (Content Type Rule Language) the conditions and actions for this rule are:
if ( (this.type == 'Motion North') && (this.camera == 'B') ) {
  var events = 
    (this.time - 20s <= 
      lookup.events.type('Motion North').camera('A').time) 
  if (events.size <= 1) {
    run('TrafficSignalControl.sh', 'ChangePhase:Red', 'A')
    run('TrafficSignalControl.sh', 'ChangePhase:Red', 'B')
  }
}

The condition in the statement above calculates a timestamp value from the incoming Directional Motion event through intersection B by subtracting 20 seconds from it:

this.time - 20s

The calculated timestamp value is used to lookup Directional Motion events whose timestamp is greater or equal to the calculated value, that is, 20 seconds earlier relative to the incoming event. 

In this case, when the conditions in the rule are met, the run action executes twice, and invokes a hypothetical TrafficSignalControl.sh shell script which is a wrapper for a program that interfaces with the traffic lights at intersections A and B. The shell script takes a ChangePhase:Red command as an argument, as well as the name of the intersection whose light to change. 



Such a rule action is entirely for illustration purposes only, and assumes that traffic lights in a city expose some kind of API that can be used to send a light phase change request from the rules engine as a result of triggering a rule. In the sample rule above, the TrafficSignalControl.sh script would be invoked once to suggest a light phase change at First St. and Main North, and a second time to suggest a light phase change at Second St. and Main North.

This example, while simple and somewhat trivial, is mainly intended to be extensible. The example shows that the rules engine, used as an extension to a traffic control system, can make smart decisions automatically. The rule logic detects a traffic anomaly and identifies exceptional conditions in a way that can trigger optimizations in traffic flow in a busy environment. The traffic gap phase change scheme is also extensible and will work for any number of intersections, at a larger, smarter city level. 

Wednesday, January 26, 2011

How to Catch a Bad Guy with 3 Lines of Code

Every rule registered in the rules engine's repository has a target. The target simply names the type of object the rule applies to. When an event comes in, the rules engine tries to match the event type to a rule target. If the targets match, the engine invokes the Ctrl interpreter and passes the event itself into the interpreter as the evaluation context for the rule's statement.

In the suspicious-circling-vehicle-detection-scenario, video analytics are applied to a video stream to generate an event of type "A" each time a red vehicle moves through the camera's region of interest in a specific direction. The event stream generated from this configuration is then fed to the rules engine.

Given such an event stream, a rule whose target is "A", and the following 3-line Ctrl statement, can be setup to detect the circling vehicle:
var events = 
  (this.time - 10m <= lookup.events.type("A").time)
if (events.size >= 4) {
  takeAction('Dispatch Patrol Car')
}
In plain English the statement roughly translates to: "if event A occurred 4 or more times in the last 10 minutes, take action, dispatch patrol car."

Breaking down the statement:

Variable Declaration
var events = 
  (this.time - 10m <= lookup.events.type("A").time)
A variable declaration, var events =, declares a variable to store a value. The Ctrl is not a strongly typed language. The interpreter automatically converts a var into the appropriate type, based on the expression to the right of the assignment. The type of value assigned to the variable in this case is an array of event.

Time Roll Expression
this.time - 10m
The event that triggers a rule is refered to in the Ctrl as "this", a special keyword. Each event has a "time" property, a timestamp, which the expression refers to as "this.time". The expression rolls back the timestamp value 10 minutes by appending - 10m, to complete the expression. In the Ctrl expressions like this one are called time roll expressions. Time roll expressions evaluate to date-time values that are calculated relative to the date-time of an incoming event. Date-time values may be rolled back or forward by seconds, minutes, hours, days, or months.

Lookup Expression
lookup.events.type("A").time
A lookup expression that resolves to an array of event, these expressions refer to persistent data. Lookup expressions reflect on the database schema and drill down into record sets (and sub-sets) using the dot "." operator. A filter, type("A"), further qualifies a lookup by narrowing down the result set to events whose type property is "A".
(this.time - 10m <= lookup.events.type("A").time)
In full form, the expression above resolves to an array of events of type "A" whose timestamp value is greater than or equal to 10 minutes prior to the timestamp of the incoming event.

If Statement

The array size check guarantees that at least 4 matching events are fetched by the lookup:
if (events.size >= 4) {
  ...
}
Rule Action

The rule "triggers" if the statement above evaluates true, and the action inside the body of the if statement executes:
takeAction('Dispatch Patrol Car')
Under normal circumstances, the suspicious vehicle would trigger the rule in real-time upon completing it's fifth circle around the block. Five times around the block is very suspicious behavior. If all goes well, the patrol car dispatched would confront nothing more menacing than a lost tourist. Otherwise, 3 lines of Ctrl code is all it takes to catch a bad guy.

Tuesday, January 25, 2011

Detecting Suspicious Activity

Events in a stream often form patterns. In previous posts, I describe a few simple algorithms that can be used to identify event patterns in a stream of events. These algorithms are focused on different types of events occurring in a sequence, chronologically. When the same type of event occurs repeatedly within a fixed time period, the repetition may also be recognized as a pattern. In some situations, multiple occurrences of the same type of event may even be considered suspicious activity.

A simple example of this is a vehicle circling around a block. Usually, when a vehicle circles around the block lots of times, something's up. Think of the creepy white van in espionage thriller movies.



In this hypothetical scenario, the video stream from a single IP camera positioned adequately to capture high quality images, is fed to video analytics algorithms that detect vehicles moving through the scene. More specifically, there are two video analytics algorithms applied here, one to detect motion in one direction, and another to recognize an object based on object type and color.

In the animation above, the red car moving through a region of interest in the camera's field of view (the green circle) generates a specific type of event. The analysis of the images generates this event each time directional motion is detected and the object type, a red car, is recognized. To simplify things further, let's say the analytics generate event type "A" when these things happen.

My next post focuses on how to detect the re-occurrence of this event within a time constraint and how to setup data conditions in the rules engine to spot this kind of suspicious activity in real-time.

Tuesday, January 18, 2011

Sequencing Algorithm, Least Recent

One of the problems mentioned in a previous post, remains unresolved. In my last post, I describe a better approach to find the most recent events in an event stream while matching a pattern. This post describes an approach to find the earliest instances of each of the elements of a pattern in a stream of events, or the least recent events.

The algorithm to find the most recent events performs essentially the same steps as the algorithm that finds the least recent ones. The key difference between the two is that the latter reads the event stream in ascending order by timestamp, and processes the elements of the pattern in order.

Like this:
String[] sequence = {"A", "B", "C"};
List<Event> result = new ArrayList<Event>();
for (String type: sequence) {
   for (Event e: events) {
      if (e.getType().equals(type)) {
         if (result.isEmpty()) {
            result.add(e);
            break;
         } else {
            Event x = result.get(result.size() - 1);
            if (e.compareTo(x) > 0) {
               result.add(e);
               break;
            }
         }
      }
   }
}
The first step processes the first element in the pattern, A, and loops over the event list until the first instance of A is matched. This event is always the first one to make it into the result list. No need to continue scanning at this point. Since the algorithm scans the event list in ascending order, all other instances of A are more recent, and should be skipped.

The next step is to find the least recent B that occurs after the A in the result list. If the least recent B has a timestamp greater than the timestamp of the A that is in the result list, then it is "in sequence"  chronologically, and may safely be added to the result:
Event x = result.get(result.size() - 1);
if (e.compareTo(x) > 0) {
   result.add(e);
   break;
}
The next element in the pattern is processed in the same manner: the least recent C that occurs after the B that is in the result list will be added. These steps are repeated until each element of the pattern is processed in order.

If the first 20 seconds of the minute long event stream we've been using as a test case looks like this:

B 10:00:00 <--
A 10:00:02
B 10:00:05
B 10:00:15
C 10:00:20
...

This version of the algorithm generates the expected output:

A 100002
B 100005
C 100020

Monday, January 17, 2011

Algorithm Win

A slightly more elaborate version of the event sequencing algorithm described in my previous post, expands on the tiny algorithm, and does a much better job at finding the most recent occurence of each event type in a pattern, without losing track of the time sequence.

This version of the algorithm uses a temporary data structure, an ArrayList<Event>, to store the result. Use of a temporary data structure allows the algorithm to keep track of the last item added into the result and to compare it to the events in the stream.

The other important distinction between this version of the algorithm and the tiny one is the order in which the pattern itself is processed: in reverse.

The most efficient way to build a sequential list of events that match a pattern, and that represent the most recent occurrences of each event type in the pattern, is to loop once through an array representing the pattern (in reverse), and within each loop, scan the event list (in descending order by timestamp) until there's an event that matches the first element out of the reversed pattern. Like this:
String[] sequence = {"C", "B", "A"};
List result = new ArrayList();
for (String type: sequence) {
   for (Event e: events) {
      if (e.getType().equals(type)) {
         if (result.isEmpty()) {
            result.add(e);
            break;
         } else {
            Event x = result.get(result.size() - 1);
            if (e.compareTo(x) < 0) {
               result.add(e);
               break;
            }
         }
      }
   }
}
The first pass through these two steps will always yield the most recent occurence of the last element in the pattern, the most recent C. The algorithm knows that it has found this particular item when an event matches the type in the current pattern element, and the result list is empty. When these two conditions are true, the result list may grow by one.
if (e.getType().equals(type)) {
   if (result.isEmpty()) {
      result.add(e);
      break;
   }
...
}
The first step then is to loop over the event list until the first occurrence of C is matched. This event is always the first to be added to the result list. It is not neccessary to continue looping over event list at this point looking for any more instances of C because all other instances of C in the list are older and can safely be skipped. Breaking the loop at this point saves the algorithm from performing unnecessary work.

Moving onto the next element in the pattern, the next step is to find the most recent B that is older than the C that is in the result list. If/when this event is matched while scanning the event list, we can safely add it to the result, break the loop, and move onto the next element in the pattern. The key to maintaining the time sequence in the result is simple: don't add an event to the result list unless it's timestamp is earlier than the timestamp of the last event added.
Event x = result.get(result.size() - 1);
if (e.compareTo(x) < 0) {
   result.add(e);
   break;
}
This step guarantees that the most recent B that has an earlier timestamp than the C that is in the result list, will be added to the result. The same step is applied to the next element in the pattern: the most recent A that has an earlier timestamp than the B that is in the result list will be added to the result.

The algorithm continues to move through the elements in the pattern, compares the timestamps of the events in the list to the last element in the result, and breaks (stops) the event list loop when a match is found. These steps are repeated until the end of the pattern is reached.

Even if the last 20 seconds of the minute long event stream look like this:

...
A 10:00:40
B 10:00:45
C 10:00:50
B 10:00:55
A 10:00:56 <--
C 10:00:59

This version of the algorithm generates the expected output:

A 10:00:40
B 10:00:55
C 10:00:59

And the flaw in the tiny version of the algorithm is removed. FTW.

One thing to keep in mind is that not all event streams show a pattern. What if the only event that occurs during the minute long event stream is A?

A 10:00:00
A 10:00:02
A 10:00:05
A 10:00:10
A 10:00:15
A 10:00:20
...

The algorithm will find the most recent A, no problem, but because there is no pattern A > B > C in the stream, an additional check should be added to validate the size of the result against the size of the pattern. The result list is valid, and should be output, only when it has as many elements as the pattern:
if (result.size() == sequence.length) {
   Collections.reverse(result);
   for (Event e: result) System.out.println(e);
}

Saturday, January 15, 2011

Algorithm Fail

Part of the work on the rules engine last year involved writing an algorithm to find a pattern of events in an event stream. The problem seemed simple to solve, initially, but the algorithm grew more elaborate as I ran different event streams through it.

The algorithm takes two inputs: an event pattern such as "event A followed by event B, followed by event C", and a linear stream of events, with a type and time of occurence:

A 10:00:02
B 10:00:05
B 10:00:15
C 10:00:20
C 10:00:25
A 10:00:30
A 10:00:35
A 10:00:40
B 10:00:45
C 10:00:50
B 10:00:55
C 10:00:59

The list above represents 12 events that happen within a one minute time span. The algorithm needs to find the events in the stream that match a pattern, within this time span or any other. It also needs to provide an option to find the least recent or the most recent occurrence of each type of event in the pattern.

To find the least recent occurrence of A, B, and C in a stream of events, the simplest approach is to loop once through each of the elements in an array representing the sequence, and within each loop, scan the event stream until there's an event that matches the type in the sequence.
String[] sequence = { "A", "B", "C" };
for (String t: sequence) {
  for (Event e: events) {
      if (e.getType().equals(t)) {
         System.out.println(e);
         break;
      }
  }
}
One pass through the algorithm produces:

A 10:00:02
B 10:00:05
C 10:00:20

This tiny algorithm also finds the most recent occurrence of A, B, and C in a stream of events, if we reverse the order of the event list (sort the list in descending order by timestamp), and give it the reversed event list:
Collections.reverse(events);
With the event list reversed, the algorithm produces:

A 10:00:40
B 10:00:55
C 10:00:59

So far, so good: the algorithm is efficient, doesn't do any more than it needs to, and gives us what we want. But real-time events don't always come in neat sequences. This approach to find the most recent occurrence of A, B, and C breaks down with just a minor variation in the stream, for example, if A happens right before the last C. The following partial stream of events from the last 20 seconds of the minute would look like this:

...
A 10:00:40
B 10:00:45
C 10:00:50
B 10:00:55
A 10:00:56 <--
C 10:00:59

With this minor variation in the stream, the simple algorithm produces:

A 10:00:56
B 10:00:55
C 10:00:59

While the events selected by the algorithm are the most recent of each event type we're looking for, the output is not in sequence. As you can see, the instance of B in the output occurs one second before the instance of A. This is a problem.

The same strategy used to find the least recent occurrence of A, B, and C also breaks down if B is the first event to occur within this time span of events. In ascending order, the stream of events from the first 20 seconds of the minute would then be:

B 10:00:00 <--
A 10:00:02
B 10:00:05
B 10:00:15
C 10:00:20
...

Given this minor variation in the event stream, the algorithm generates:

A 10:00:02
B 10:00:00
C 10:00:20

Again, the output represents the least recent of each event type in the pattern, but the instance of B in the output occurs two seconds prior to the instance of A, so the algorithm is flawed because it loses track of the time sequencing when it moves to the next element in the pattern and breaks the sequence. We need a better strategy.

The next post explains a better, slightly more elaborate strategy.

Wednesday, January 12, 2011

Rounding Error

During regression tests earlier this week, our test team spotted a bug in our rules engine. I spent part of the day today getting to the bottom of it.

The rules engine can be setup to monitor event data as it streams in, and to identify event patterns. The engine tries to find a pattern defined by a rule in a stream of events. In this sense, rules are to streams of events as regular expressions are to strings of text.

The test team registered a rule for a simple pattern to look for: event A, followed by event B, followed by event C, occurring in a 2 minute time span. Pretty simple, straightforward stuff. The bug looked suspicious to me from the start because it fell into an area of code that had received considerable attention during our integration tests, and in my mind was solid.

The expected output from the engine, given this rule definition, is a set of detail records that capture the participant events in the sequence, in order. The engine creates a detail record for A, B, and C plus a parent D record. But the test team discovered that in some cases, detail records captured a slightly different sequence, most of the time with events A and B inverted.

After looking over the code in question, and having taken a closer look at the event data, the problem turned out to be a rounding error. At some point we had decided to round down centiseconds from DB2 timestamp values before sorting events, because we considered sub-seconds insignificant to real-time events. But the rounding was precisely the root of the problem today since we were seeing event timestamps with differences in the 1/100ths of a second.

Tuesday, January 11, 2011

Code Reuse

One of the advantages of building a model around a programming language and compiling source code to the model is reuse. For example, the Ctrl syntax allows the loop condition (or loop test) in a for loop to take on any of the classes in the model that extend RuleCondition:

i == 10EqualCondition
i != 10NotEqualCondition
i < 10LessThanCondition
i <= 10LessOrEqualCondition
i > 10GreaterThanCondition
i >= 10GreaterOrEqualCondition

When used as the loop test inside a for loop, these expressions evaluate the same way as when they are used inside an if statement because the evaluation model code behind the expressions is the same, regardless of the statement type and context:
for (int i = 10; i != 10; i--) {
    ...
}

if (i != 10) {
    ...    
}
In both examples above, the i != 10 expression compiles to an instance of NotEqualCondition whose evaluate() method compares the value of the "i" variable to the value on the right side of the expression, and returns true if they are not equal.

Monday, January 10, 2011

For Loop Evaluated

Focused almost all of my attention today writing and lightly exercising the Java code in the Ctrl model that supports the evaluation of a for loop. As mentioned in a previous post, Ctrl source code compiles to an instance of RuleStatement which is a base class in the language evaluation model.

The Ctrl is interpreted in the sense that statements read from source code are translated by ANTLR-generated parsers directly into a Java object called a RuleStatement to be evaluated in memory by the Ctrl interpreter. There is no intermediate representation, other than a fully initialized RuleStatement, that is used to execute Ctrl code.

In the model, as a descendant of RuleStatement, ForStatement overloads evaluate(). Within this method, ForStatement initializes the loop variable, evaluates the loop condition, processes the loop counter, and returns a boolean value, or throws an EvaluationException.

Having implemented evaluate(), I was able to eval-test two types of for loops, one that counts up, and another that counts down:
for (int i = 0; i < 10; i++) {
  ...
}

for (int i = 10; i > 0; i--) {
  ...
}
Both loops evaluate as expected, the value of the "i" variable increases or decreases by 1 with each pass through the loop and the body of the loop executes 10 times. This being the case, the for loop evaluation code I wrote today seems to be implemented correctly.

Friday, January 07, 2011

For Loop Compiled

Made a pass today at updating the tree grammar for the Ctrl to include a description of the for loop AST structure. The structure of the tree was pretty much copied verbatim from the parser grammar, with a few minor tweaks. ANTLR uses the same notation for both types of grammars so tree structures are easily ported between the two.

During tree traversal, while visiting a node in the tree, the tree walker executes an "action". In our tree walker, an action is a transformation step that goes from AST to an instance of a Java object from our Ctrl model. The action essentially takes node values from the AST and copies them into a RuleStatement. This is what Ctrl compilation comes down to. A few getters and setters were missing, so I added these as well as toString() implementations to all of the Java classes in the model supporting the for loop.

I took a screenshot of my debugging session. The breakpoint in the screenshot is set in my TestRig. Whenever the grammars are touched, I run the TestRig. It is a suite of tests that compiles a wide range of statements in the Ctrl, from simple one-liners, to complex multi-line if-then-else statements.



We are in good shape so far. The screenshot shows a compiled RuleStatement object that contains an initialized ForStatement, in the "Variables" view of the debugger.

Wednesday, January 05, 2011

For Loop Redux

Added several new elements to the ANTLR grammar for the Ctrl today. With these additions, the ANTLR-generated parser recognizes a for loop from the following basic syntax:
for (int i = 0; i < 10; i++) {
  ...
}
We need a for loop in our language to allow blocks of code to be executed repeatedly for some iteration within the context of a rule. This basic loop declares a counter to control the number of iterations and to expose the counter's value to the code inside the loop. As with for loops in other programming languages, the Ctrl loop has a loop-test declaration, i < 10, which may be any type of conditional expression that evaluates to a boolean value to stop the loop when the expression evaluates to false.

A few simple tests against the new grammar rules that recognize this basic loop are good so far.

Out of curiosity, I looked up the Wikipedia entry for for loop. The article includes a flowchart diagram of a for loop's behavior at run time:



During my syntax tests today, and to my delight, I discovered that the parser recognizes another for loop or an entirely different statement where D appears in the diagram above, that is, it recognizes a full statement nested inside the body of the loop. For example, the parser also reads statements like this without any fuss:
for (int i = 0; i < 10; i++) {
  if (color == 'blue') {
    for (int p = 0; p < 20; p++) { 
       ... // and so on
    }
  }
}
This means that if statements inside for loops and for loops inside if statements are syntactically valid constructs in Ctrl which was entirely unintentional side effect of the grammar changes.

Even though nested statements are not yet in the immediate requirements that I'm addressing with these changes, I think they provide the Ctrl with great expressive power so I'm definitely going to leave this feature in the grammar.

The other noteworthy observation about the changes in the grammar is that the abstract syntax trees built by the ANTLR-generated parser are composed from succinct little subtrees that come out of applying the new grammar rules to a statement. The example above is translated by the parser into a tree with a two-dimensional structure shown below. The tree was drawn for me courtesy of ANTLRWorks and shows how the statement breaks down syntactically:



Tomorrow's task will be to add rules to the ANTLR TreeWalker to traverse and to translate a tree like this into an instance of a RuleStatement in the Content Type Rule Language Java model, which is what the Ctrl compiles to.

Tuesday, January 04, 2011

Complex Event Processing

We added a home-grown rules engine into our software last year as a sub-system in order to provide Complex Event Processing (CEP) capabilities. CEP deals with complex event pattern detection, event correlation, as well as the causal and temporal relationships between events. CEP examines small events taking place across a busy environment, like a city, and is typically used to analyze impact at a higher level, with the goal of giving users the ability to take action in real-time when confronted with changing conditions. 

Our rules engine monitors event data as it streams into the system from a variety of sources, mainly from IP cameras. Rules can be setup to determine whether or not an event is of interest, or part of a larger pattern. 

The focus of last year's release was on providing CEP for sequences of events. For example, we can now setup rules to look at incoming events and determine if they were preceded by a related event within a defined time constraint. This gives our customers the ability to correlate sequences of events, sometimes looking for cause and effect, which would otherwise go by unnoticed. The rules engine can be setup to handle a myriad of scenarios involving sequenced events.  

One of the simplest scenarios we use for our tests takes place in a hypothetical Jewelry Store. A single rule is setup to detect if a car speeding out of a Jewelry Store parking lot is preceded by someone bolting out the door which itself is preceded by a valuable object being removed from a shelf, within a certain time constraint defined in seconds. This sequence of events, one happening after the other, in the Jewelry Store context is of interest, of course, and likely to be carried out by a thief. When this happens, the rules engine correlates, sends out a notification, and creates a fourth, derivative event that represents a composite of the other three. 

Monday, January 03, 2011

For Loop

Last year I spent a considerable amount of time thinking about, designing, and building an interpreter for a small programming language called the Content Type Rule Language (Ctrl). The language itself is tiny, consisting of little more than if-then-else statements. The Ctrl turned into a special purpose programming language or Domain Specific Language that includes expressions specific to our rule engine's problem space.

In the next iteration, the Ctrl will be extended to include several new features including variable declaration, variable assignment, and a for-loop. A for-loop is needed in order to execute blocks of code repeatedly, and to support rules that are triggered by a counter. Because a counter in the Ctrl will be declared as a variable, the language needs variables too.

My goal in the next few blog posts is to explain how I intend to add these new features to the language. 

Welcome

With the new year comes renewed enthusiasm. This year starts with a new blog that is intended to capture some of my daily thoughts. Welcome.