This page explains what Rules are and how they work. It also explains how Options work, since they are very similar to Rules.
The parts of a Rule
A Rule can be represented in JSON format. An example is given below.
A Rule has three main parts:
The dependencies are a list of Symbols, each with a weight and optionally a string for further filtering. They determine whether or not a Rule is considered for execution at all.
If all Symbols in a dependency have been signalled with '!set_signal_weight' Tags, then the Rule is considered for execution. However, it is first checked if the next part of the Rule, the trigger, also matches.
The dependencies of a Rule act as a prefilter. Unlike the trigger, checking if a Rule's dependencies are fulfilled can be done quickly and does not slow down Elody.
Some more details about the way this works
If necessary, this can be made more complex: Dependencies can also have weights and filter strings, '!set_signal_weight' Tags can have weights and comments, and Rules can have a threshold.
Each dependency of a Rule is considered active only if a '!set_signal_weight' Tag exists that matches the dependency. The dependency matches if the Symbols are the same and the filter string of the dependency is either not required, or is equal to the comment of the '!set_signal_weight' Tag.
The weight of an active dependency is the product of the '!set_signal_weight' Tag's weight (defaults to 1) and the dependencies weight. It can be negative.
If the sum of the weights of all active dependencies of a Rule is above a threshold, then the Rule passes
(If this all sounds confusing, don't worry, it's pretty straight-forward in practice and you usually can just list the dependencies you need without worrying about weights.)
The trigger of a Rule has two purposes: It confirms whether or not the Rule should be executed, and it finds arguments for the execution.
The trigger is able to access any of the objects that have been created in the Scenario so far. It can look for patterns among those objects to check if the Rule is applicable.
In addition to filtering objects to decide whether or not the Rule should execute, the trigger can also capture objects to be used as arguments by the Rule.
The trigger can perform pattern matching. Most Rules use very simple patterns, but if necessary a trigger can become very complex.
The actions of a Rule are executed only if the dependencies exist, the trigger finds a set of objects that match its requirements, and Elody determines that this Rule, out of all the other candidate Rules, should be executed next.
Through its actions, a Rule can create Tags, run programs, create messages, create Options, and many things more.
A short example
Let us consider a simple Rule as an example. The purpose of this Rule is to perform a prediction on a time series, using a program that can work with CSV files.
The Rule has two dependencies: It looks for two Symbols. One that means "a prediction of a time series is wanted" and one that means "This file is a CSV file". Tags with these two Symbols are created by other Rules and Programs. The Rule is only used if both dependencies are found.
The Rule has two triggers: Firstly, it looks for a File that has a Tag with the aforementioned Symbol attached to it. Secondly, it also looks for a File that has a Tag attached to it with a Symbol that means "this file describes parameters that should be used for a prediction". The latter of the two is marked as an optional argument, since it is not strictly necessary.
The Rule has one action: it executes a Program that can perform a prediction. It passes the above two Files to the program. If the File with the parameters could not be found, the Program is told to use default parameters instead.
This is the general format of a Rule. An example of how this looks in code can be found on the next page.
Multiple versions of the same Rule
You can define multiple Versions of the same Rule.
When Elody searches for applicable Rules, if several versions of the same Rule pass the Dependencies checks and the filters set by the user, only the latest of them will be considered further.
For example, you may have a Rule that is fairly popular and has received a lot of positive feedback, so Elody uses it a lot. However, you want to make a small change to it and upload a new version. The rating of this new version is independent of the rating of the previous version. Elody will automatically use the latter version if its rating passes the safety parameters of the user, and will resort to the previous version otherwise.
It is done this way for security reasons: Without this, a malicious developer could upload a harmful rule as a new version of an established rule to piggy-back off its popularity. Since the new version has to gain feedback independently of the first, this can't happen.
(This website is still in Beta. This may be changed later depending on how well it works.)
Options, and how they differ from Rules
Options are similar to Rules, but not the same.
They serve multiple purposes, such as encapsulating planned actions so that they can be interacted with, priotitizing different actions relative to each other, and presenting a choice to the user.
Where Rules are constant and shared between Scenarios, Options are created within a Scenario and specific to it.
Options have triggers and actions, just like Rules do. They do not have dependencies.
Options can have a display, which is used to render a Message to the user if Elody determines that the user should choose between several possible Options to execute. This is not required, as some Options are not intended to be presented to the user.
While Rules are only considered for execution if their dependencies match with '!set_signal_weight' Tags, Options are only considered if '!set_option_confidence' Tags are applied to them directly. In this way, the importance of an Option can be adjusted by other Rules, Options and Programs simply by assigning '!set_option_confidence' Tags to it.
The prioritization of Rules and Options is closely connected:
Most of the time, Rules are given priority. If however the quality/rating of the best available Rule is below some threshold (determined by parameters of the Scenario), Elody will look at the Options instead. If an Option's rating is high enough, it is executed right away. If no Option is rated quite high enough, Elody will instead present all Options with some minimum rating to the user so that they can choose what they want.
Why does it work like this?
The intent is that Rules should almost always work indirectly:
Rather than executing a Program directly, a Rule should create an Option that will execute that Program later. As Rules take priority over Options, a number of different Options will be created before any Programs are executed at all. Since Options are objects that are available for inspection by a Rule's or Option's trigger, Rules and Options can react to the presence of existing Options. This allows Rules to react to what other Rules were planning, interact with each other and suggest alternatives. The examples on the following pages should make this clearer.
The next couple of pages will give examples of Rules, Options and Programs in practice. After that, see here for a complete documentation.
Elody Decision Process messages
When you are in Developer Mode in a Scenario, you will see some Messages titled "Elody's decision process". One of these messages is created each time Elody chooses which Rule or Option to execute next.
When you are reading other people's Rules or writing your own and you find yourself confused, these should be your first stop for figuring things out.