The cDMN notation is based on the DMN standard, an open standard by the Object Management Group (who also specify the UML standard). If you already know standard DMN, learning cDMN shouldn’t be too hard. If you don’t know DMN, getting the hang of cDMN might take some practice. However, after making a few examples, you will be able to model your own problems in cDMN without any worries.
1. cDMN vs DMN¶
cDMN adds four main features:
- constraint reasoning;
- more expressive data;
- data tables.
These additions are further explained in the following subsections.
2.1 Constraint reasoning¶
By allowing to set constraints in the models, more complex problems can be modeled in a readable manner. Moreover, constraint tables are not forced to set a default value when no rule applies. Because of this, rather than define a single solution, a cDMN implementation defines a solution space.
Quantification allows for creating more compact and maintainable tables. Instead of a row only applying for a specific element, it can now apply to every element part of a specific subset.
2.3 More expressive data types¶
Standard DMN variables are 0-ary functions (known as constants). They require no argument (thus 0-ary) and return a single value. cDMN adds three more data types:
These allow for a more flexible usage of knowledge and for a better design of tables.
2.4 Data tables¶
When modelling a problem in cDMN, we divide it in two parts:
- the problem logic;
- the actual problem instance, i.e. what specific problem to solve.
For the map coloring problem, in which we want to color a map so that no two countries share the same color, we have the following division:
- Problem logic: color a map so that no two countries share a color;
- Problem instance: the specific map to color.
In cDMN, the problem instances are represented via data tables. This allows for the application of a single cDMN model to multiple problems, by simply switching out the data tables.
Before writing any business logic, rules or constraints, a specific set of terms need to be defined. We use multiple glossaries for this purpose, with each glossary a list of specific kinds of terms. In total there are 5 different glossaries:
|Type||A type can be seen as a domain of possible elements.||Name, Type, Values|
|Function||Functions map an amount of types on a single type.||Name, Type|
|Constant||A constant maps a name on a single type.||Name, Type|
|Relation||Relations specify a connection between types.||Name|
|Boolean||Booleans map a type on either True, or False.||Name|
More explanation of the 5 different glossaries can be found in the rest of the section, including examples.
A type can be seen as a definition of a domain of possible elements.
For example, all integer numbers can be grouped as one type.
Or, all the people working at a company could be grouped as the type
cDMN knows 4 basic types, which can be used in the glossary without needing explicit definitions. All other types need to be derived from these basic four types (subtyping).
|Int||An integer number, ranging from -∞ to +∞.|
|Float||A number containing a comma, ranging from -∞ to +∞.|
|Datestring||A representation for dates, in the OSI 8601 format. (this is currently experimental)|
These basic types are used to create subtypes. This allows a more readable notation, and a reduction of possible values of a type.
For instance, in the following snippet the type glossary of the
Reindeer_Ordering challenge is shown.
Here, we define 3 types:
|Reindeer||string||Comet, Rudolph, Prancer, Cupid, Donder, Vixen, Dancer, Dasher|
Subtyping has 2 main advantages:
- The name of the type is easier to interpret (
- It’s possible to set a more narrow set of possible values of a type.
string in itself could be anything: e.g. it could be
string into the type
Reindeer, we can narrow down the possible types to just the names of the reindeer.
The same is done for
Place, which is a subtype of integers.
Subtyping here allows us to set a range of integers, which makes sure that there can never be a
Place outside of the range
Name column, we write the name of our type.
This name can be any string, but should be something descriptive of the type it’s specifying.
Type column, we can refer to the supertype we’d like to use.
This can be one of the 4 basic types, or another type that is already declared in the glossary.
In the last column,
Values, we can write down all the elements that are containing in our type.
For instance, for the type
Reindeer, these are the names of all the reindeer.
Place, we define a range of integers, from one to nine.
Thus, our range is defined as the following values:
1, 2, 3, 4, 5, 6, 7, 8, 9.
A well-defined range can make the difference between a short and a long solving time.
The concept of subtyping can be pushed even further: it’s possible to create subtypes of subtypes of subtypes, ad infinitum.
For string, the column values can be left undefined by inserting a
- or by referring to a data table containing those values.
In both cases, there should be a data table defining the string. See 3.4 Data tables.
Subtypes of floats and integers should always have a defined range.
A function in cDMN is a mapping of arguments on a single type. This is especially useful when there’s a functional link between two sets of elements. For example, a set of people can be easily mapped on a set of dates of birth using a function. Every person has exactly one date of birth, but a date of birth can have multiple people being born on it.
The term “function” in cDMN is not be confused with “function” in programming. cDMN uses the mathematical meaning.
Name column, we write down the name for our function.
However, this name can not just be anything: there is a specific syntax for functions.
The syntax is fairly straightforward: it can be any name as long as the argument types are clearly part of it.
value of Argument is a function to map an instance of
Argument on something else.
It’s possible to list multiple arguments, each of which should be a known type.
For instance, in the Reindeer problem our Function glossary could look as follows.
|order of Reindeer||Place|
|Partial relativeposition of Reindeer and Reindeer||Relative|
order of Reindeer will assign a value of the type
Place to each
It makes sense to use a function for this purpose, as each reindeer needs a position, and can only have a maximum of one position.
There is a direct connection between the two types.
It’s currently impossible to use
int as a super type for a function, because it doesn’t have a set range of values. You should always define a subtype of integers, and assign it a correct range.
Make sure that there are no other types in the name other than the argument types, or the solver will add them to the list of arguments. For example,
place of Reindeer represents a function with two arguments, namely
In order to only have the
Reindeer argument, write the
Place with a lowercase:
place of Reindeer.
A partial function acts like a normal function, except that not every argument needs to map to a value. Hence the name partial function.
Take extreme caution when using a partial function. When quantifying over a partial function, make sure you only use the arguments for which a value is set in the partial function.
The constant is a special type of function, where no argument is used.
It only contains one value, instead of a value for each type of argument.
This can e.g. be used when defining a cost for a problem, as demonstrated in the following glossary snippet.
Note how the keyword
of isn’t used.
A relation can be seen as a function and produces either
Unlike the function, the relation has no specific syntax. Any string can form a relation, as long as it contains type names. There can be as many types listed as needed, in every possible order with any possible word in between.
For example, in the
Map_Coloring challenge, the glossary is as follows.
|Country border Country|
A special type of relation is the boolean. It’s a relation which doesn’t have any arguments, and it can only be
3.2 Decision tables¶
The decision table is the bread and butter of DMN/cDMN. It allows the user to model a basic decision, in the form of a simple table. An example of an annotated decision table can be found below.
The table decides for every employee whether or not they are eligible for extra vacation days. The text it models is listed in the following example.
Example: vacation days eligibility
All employees under 18 years or over 60 get 5 extra vacation days (they apply for rule1). If an employee is between those ages, but has more or equal to 30 service years, they also get the extra days.
This example is implemented as three rows in the decision table.
It checks for every employee (the
- translates to a
don't-care, thus for every employee), every age of employee and every number of service years of employee whether or not they’re eligible for the vacation days.
A translation of the table rules to English would be as follows:
- Every employee, younger than 18, with any amount of service years is eligible.
- Every employee, older or equal to 60, with any amount of service years is eligible.
- Every employee, between age 18 (included) and 60 (excluded) with more or equal to 30 years of service years is eligible.
If for a specific employee none of the rules are satisfied, the relation
Employee is r1 is automatically considered as untrue. There is no seperate rule needed to define this.
This specific decision table has the
Unique hitpolicy, denoted by the
U in the top-left corner of the table.
This means that at most only 1 row can be satisfied, and there is no overlap between the rows.
For a table of other hitpolicies, see 3.2.1 Hitpolicies.
A decision table can have any amount of inputs (even zero), and needs at least one outputcolumn.
|U||Unique: only one rule can be satisfied.|
|A||Any: any of the rules can be satisfied.|
|F||First: only the first rule eligible is satisfied.|
|C+||Sum: sum the values of each satisfied row.|
|C<||Minimum: take the minimum value of each satisfied row.|
|C>||Maximum: take the maximum value of each satisfied row.|
|C#||Count: count the amount of satisfied rows.|
For more examples on these hitpolicies, see the Examples.
3.2.2 Using the same type twice¶
In cDMN it’s possible to use the same type as many times as you like in a single table.
To differentiate between multiple instances of the same type, the syntax
Type called typename is introduced.
The following table shows an example.
|U||Person called p1||Person called p2||p1 Likes p2||p2 Likes p1||p1 Matches p2|
In text, this table reads as: “For every Person p1 and Person p2, if p1 likes p2 and p2 likes p1, then p1 matches p2.”
Note how we have to explicitly define that p1 does not equal p2.
3.2.3 Syntax in cells¶
Inside a decision table cell, only a certain syntax is allowed. The below table shows every possible syntax that a cell can have.
|-||don’t care: the rule goes for every possible value of the inputcolumn.|
|Yes/No||Can be used when trying to specify the status of a specific relation.|
|not(type)||(function): Specifies that it can’t be the same value.|
|not(relation)||returns the inverted value of the relation.|
|val||usefull when you want a rule to only count for one specific value of a type.|
|<= x; >= x; < x; > x; = x||Used to compare two values.|
|[x,y]||inclusive range, the value needs to be between x and y or equal to x or y.|
|(x, y]||the value needs to be between x and y, or equal to y.|
|[x, y)||the value needs to be between x and y, or equal to x.|
|(x, y)||the value needs to be between x and y.|
|#Type||the number of elements in type
3.3 Constraint tables¶
A constraint table is used when a list of constraints needs to be modeled.
It can be seen as a decision table in which each row needs to be satisfied.
It is denoted by the “Every” hit policy, as in “every rule needs to be satisfied”.
In tables, it is denoted by
|Bordering countries can't share colors|
|E*||Country called c1||Country called c2||c1 Borders c2||Color of c1|
|1||-||-||Yes||not(Color of c2)|
For instance, in the Map Coloring problem, we want to define that if two countries share a border, they should have a different color. In text, this is written as “For every country c1, country c2 holds that if c1 and c2 share a border, the color of c1 cannot equal the color of c2”.
It’s also possible to define multiple constraints, where each one needs to be satisfied. This is shown in the Monkey Business example.
|E*||Monkey||place of Monkey||fruit of Monkey||place of Monkey||fruit of Monkey|
This table translates into the following rules:
- For each monkey, if the monkey is named Sam, then the place of the monkey is Grass and the fruit is not Banana.
- For each monkey, if its location is Rock, then the fruit of the monkey is Apple.
- For each monkey, if its fruit is Pear, the place of the monkey is not the Branch.
- For each monkey, if the monkey is named Anna, its place is the Stream and it’s fruit is not the Pear.
- For each monkey, if the monkey is named Harriet, its place is not the Branch.
- for each monkey, if the monkey is named Mike, then its fruit is the Orange.
3.4 Data tables¶
The data table allows the user to input data that doesn’t really fit in a decision table.
Although technically you could use the
Unique hitpolicy to input data, a data table will always work faster.
Another advantage is that not every instance of a type needs to be listed in the
values column of the glossary.
If any of the decision tables or constraint tables holds a specific type value (for instance, the name of the monkeys in the previous table) you still need to explicitly list those values in the glossary.
A simple example of a data table is the one found in Hamburger Challenge. Per ingredient, we input its sodium, fat and calories amount and the cost.
|Data Table: Nutritions|
|Item||sodium of Item||fat of Item||calories of item||cost of Item|
A data table can also do most of the things that works in a normal decision table/constraint table. For instance, it can also quantify over the same type multiple times.
|Data Table: bordering countries|
|Country called c1||Country called c2||c1 Borders c2|
|1||Belgium||France, Luxembourg, Netherlands, Germany||Yes|
|3||Germany||France, Denmark, Luxembourg||Yes|
As mentioned earlier, a data table also allows us to leave the
Values column empty for a type in a data table.
This is especially useful when we have a lot of data.
For example, in the Balanced Assignment challenge we have a list of 210 people.
Each person has a name, department, location, gender and title.
Without a data table, each of these types needs to be listed in the
Instead of having to type all this data, we can just copy-paste the employee list into a data table!
When using data tables to set the values of a function, you should make sure that it is complete. In other words, every possible set of inputs for the function should have a defined output.
In standard DMN, there is always only one solution possible for every set of inputs.
In cDMN however, we do not define a single solution but rather a solution space.
To specify which specific solution we want from this space, the
Goal table is used.
At the moment, there’s three possible goals.
|Get x models||This will find x amount of solutions.|
|Minimize val||This will find the solution with the lowest value for val.|
|Maximize val||This will find the solution with the highest value for val.|
An inference method can be specified using an
Some examples are given below.
Goal table is specified, cDMN will default to finding one model.
|Get 10 models|