openEHR logo

Decision Language specification

Issuer: openEHR Specification Program

Release: PROC latest

Status: DEVELOPMENT

Revision: [latest_issue]

Date: [latest_issue_date]

Keywords: expressions, rules, guidelines

openEHR components
© 2020 - 2022 The openEHR Foundation

The openEHR Foundation is an independent, non-profit foundation, facilitating the sharing of health records by consumers and clinicians via open specifications, clinical models and open platform implementations.

Licence

image Creative Commons Attribution-NoDerivs 3.0 Unported. https://creativecommons.org/licenses/by-nd/3.0/

Support

Issues: Problem Reports
Web: specifications.openEHR.org

Amendment Record

Issue Details Raiser, Implementer Completed

PROC Release 2.0.0 (unreleased)

0.6.0

SPECPROC-41. Add Decision Language specification.

T Beale

02 Jan 2021

Acknowledgements

Primary Author

  • Thomas Beale, Ars Semantica; openEHR Foundation Management Board.

1. Preface

1.1. Purpose

This document specifies the openEHR Decision Language, in both abstract syntax form and as a related model. The latter defines the semantics of a first order predicate style logic that can be used to write Decision Logic Modules (DLMs) containing rule-sets that may be used standalone or with a Process / Plan oriented system such as openEHR Task Planning.

The intended audience includes:

  • Standards bodies producing health informatics standards;

  • Academic groups using openEHR;

  • Solution vendors.

Prerequisite documents for reading this document include:

Related documents include:

1.3. Status

This specification is in the DEVELOPMENT state. The development version of this document can be found at https://specifications.openehr.org/releases/PROC/latest/decision_language.html.

Known omissions or questions are indicated in the text with a 'to be determined' paragraph, as follows:

TBD: (example To Be Determined paragraph)

1.4. Feedback

Feedback may be provided on the process specifications forum.

Issues may be raised on the specifications Problem Report tracker.

To see changes made due to previously reported issues, see the PROC component Change Request tracker.

1.5. Conformance

Conformance of a data or software artifact to an openEHR specification is determined by a formal test of that artifact against the relevant openEHR Implementation Technology Specification(s) (ITSs), such as an IDL interface or an XML-schema. Since ITSs are formal derivations from underlying models, ITS conformance indicates model conformance.

2. Overview

The openEHR Decision Language (DL) and model defines a formalism for expressing decision logic and rules used by process-oriented healthcare information systems, active forms and so on. Decision logic usually sits within a larger environment of plans, guidelines and data sources such as the EHR, as described in the openEHR Process and Planning Overview. The primary encapsulation of DL is within a Decision Logic Module (DLM).

Within the openEHR ecosystem, Task Plans and GDL3 guidelines both require a way of expressing rules and declaring input variables. Guidelines additionally need a way of declaring output variables. These needs are achieved with the flexible use of a single kind of multi-section module called a decision logic module (DLM).

The Decision Language is a high-level language in which modules containing the following elements may be written:

  • reference data: domain constants;

  • input variables: data items representing external variables relating to the subject (i.e. patient);

  • conditions: Boolean-returning domain-specified criteria;

  • rules: domain-specified rules structures based on condition/action structures, which may return any type;

  • rule-sets: related sets of rules in various forms, including decision tables;

  • outputs: computed results of rule invocations, including logic trace information;

All symbolic elements, including constants, variables and rules may be treated as codes for which a translation terminology is supplied, enabling a DLM to be translated and presented in any natural language.

2.1. Requirements

The requirements of openEHR Decision Language fall into a number of categories, described below.

TBD: this section only very rough for now.

2.1.1. Caller Interaction

  • the ability to capture the reasoning chain for presentation to a caller as a justification;

  • the ability to provide a decision and its possible outcomes, with the current recommendation indicated;

2.1.2. Representation

2.1.3. Binding to Data Context

  • bindings that connect variables to external data sources;

2.1.4. Type System

  • an ability to use externally defined information models that supply types that may be used in DL statements.

2.2. Execution Model

TBD:

3. DLM Syntax

The openEHR Decision Language (DL) is primarily used to write Decision Logic Modules (DLMs). A DLM has the following structure.

dlm <form> <identifier>

definitions -- Descriptive

    |
    | lang and translations meta-data, as for archetypes
    |
    language = {
            original_language: [ISO_639-1::en],
            translations: {...}
        }
        ;

    |
    | description meta-data, as for archetypes
    |
    description = {...}
        ;

[use_model
    <reference model ids; defaults to openEHR Foundation & Base types>]

[use
    <local identifier>: <dlm identifier>
    <local identifier>: <dlm identifier>]

[preconditions
    <conditions that act as pre-conditions for this module>]

[definitions -- Reference
    <constant definitions>]

input -- Administrative State
    <administrative subject variables>

input -- Historical State
    <historical subject variables>

input -- Tracked State
    <real-time subject variable>

rules -- Conditions
    <conditions expressed as EL functions>

rules -- Main
    <computational rules expressed as EL functions>

rules -- Output
    <output rules expressed as EL functions>

definitions -- Terminology
    terminology = {...};

Many of the sections are optional and are only used in specific forms of DLM, indicated by the <form> on the first line.

The definitions — Descriptive and definitions — Terminology sections (which are logically identical data structures to the same-named sections in openEHR archetypes, but in a JSON5 superset form) can be omitted for DLMs in development for which no descriptive meta-data or terminology is needed.

3.1. Identifiers

All identifiers in a DLM are strings with no whitespace. If the terminology section is present, these identifiers are included with linguistic definitions and translations, in the same way as archetypes. This approach enables the symbolic variables such as is_type1_diabetic to be used in rules, and to have a full, formal, multi-lingual definition, such as the following:

definitions -- Terminology

    terminology = {
        term_definitions: {
            "en" : {
                "resting_heart_rate" : {
                    text: "heart rate at rest",
                    description: "..."
                }
                ...
                "SpO2": {
                    text: "Oxygen saturation";
                    description: "..."
                }
            }
        }
    }

3.2. Identification Section

The identification section takes the form dlm <form> <identifier>, where:

  • <form>: flavour of DLM artefact, with value ruleset | guideline;

  • <identifier>: identifier, in the format <concept>.vN.N.N where vN.N.N represents a numeric version of the logical form major.minor.patch, and whose values over time obey the Semver.org rules.

The following shows the identification line for a guideline:

dlm guideline chadvasc2.v1.0.5

A reference to a DLM may be effected by quoting the whole identifier, or a form with the minor and / or patch parts of the version missing. Either of these forms identify the most recent matching DLm available. For example, the reference NHS-chops14.v4 identifies whatever the most recent minor version of the v4 major version of the NHS-chops DLM is locally available.

3.3. Language and Description Sections

The language and description sections are the same as for openEHR archetypes, and are formally defined by the openEHR Resource specification. The following provides an example.

definitions -- Descriptive

    language = {
            original_language: [ISO_639-1::en]
        }
        ;

    description = {
            lifecycle_state: "unmanaged",
            original_author: {
                name:           "Thomas Beale",
                email:          "thomas.beale@openEHR.org",
                organisation:   "openEHR Foundation <http://www.openEHR.org>",
                date:           "2021-01-10"
            },
            details: {
                "en" : {
                    language: [ISO_639-1::en],
                    purpose: "To record an individual's QRISK3 score."
                }
            },
            copyright:  "© 2021 openEHR Foundation",
            licence:    "Creative Commons CC-BY <https://creativecommons.org/licenses/by/3.0/>",
            ip_acknowledgements: {
                "ClinRisk" : "This content developed from original publication of
                    © 2017 ClinRisk Ltd., see https://qrisk.org",
                "QRISK" : "QRISK® is a registered trademark of the University of Nottingham and EMIS"
            }
        }
        ;

3.4. Use_model Section

The optional use_model section enables a DLM to specify a model that defines the type system for the DLM. The identifier of the model must be resolvable to a BMM model, i.e. a model for which an openEHR Basic Meta-Model file is available. This might be a well-known model such as the openEHR Reference Model (BMM form), or a custom model created for local use. The model identifier must include a version matching part, with at least the major version present. The following example specifies the use of the openEHR RM in the latest 1.1 version available.

use_model
    openEHR-RM.v1.1

The types defined in all 'used' models become available to the DLM for use in declarations. If no model is specified, the openEHR Base and Foundation types are assumed.

3.5. Use Section

A DLM can use other DLMs, by declaring each DLM with an identifier in the use section. The identifier must include a version-matching part. The following example declares the identifier BSA as a convenient local label to refer to the latest version of the DLM Body_surface_area.v1.

use
    BSA: Body_surface_area.v1

3.6. Preconditions Section

The preconditions section is used to state logical conditions that must evaluate to true for the DLM to be used for the subject. Any Boolean-typed identifier may be used from the input, condition or rules sections, or any Boolean-returning logical expression referencing any identifiers declared in the DLM. A typical preconditions section is shown below:

preconditions
    is_pregnant

3.7. Reference Section

The reference section of a DLM contains what might be thought of constant definitions, i.e. identifiers declared with fixed values. The following illustrates.

definitions -- Reference

    paracetamol_dose: Quantity = 1g;
    chlorphenamine_dose: Quantity = 10mg;
    prednisolone_dose_per_m2: Quantity = 40mg;

These may be data structures of significant complexity, such as the following risk tables.

definitions -- Reference

    Snoking_interaction_scales = {
            [female]: {
                 [age_1]:   {
                    [non_smoker]:                   0,
                    [ex_smoker]:                   -4.70571617858518910,
                    [light_smoker]:                -2.74303834035733370,
                    [moderate_smoker]:             -0.866080888293921820,
                    [heavy_smoker]:                 0.902415623697106480
                },
                [age_2]:   {
                    [non_smoker]:                   0,
                    [etc]:                         -0.0755892446431930260000000
                }
            },
            [male]: {
                 [age_1]:   {
                    [non_smoker]:                   0,
                    [etc]:                         -0.2101113393351634600000000
                },
                [age_2]:   {
                    [non_smoker]:                   0,
                    [etc]:                         -0.0004985487027532612100000
                }
            }
        }
        ;

3.8. Input Section

3.8.1. Subject Variables

The input section contains declarations of all subject variables used by a DLM. At a minimum, a subject variable declaration states the symbolic name and type of the variable in the manner typical of a typed programming language, as exemplified by the following:

input
    heart_rate: Quantity

Although a subject variable declaration appears to declare a simple property of a type such as a Quantity, in fact it creates an instance of a proxy object described below in Section 4, that provides access to snapshot values of the variable over time, as well as other smart facilities including null-value detection and range conversion, described in the sections below.

3.8.1.1. Variable Naming

The naming of a subject variable is important, and should reflect its intended domain meaning with respect to the guideline or plan which it formalises. Thus, a cardiology guideline might use a variable systolic_bp to mean 'current instantaneous systolic blood pressure' and a variable target_systolic_bp to mean a target pressure for the patient to aim for over the course of hypertension treatment. However a guideline that refers to different systolic blood pressures, e.g. historical, average and current might use variables such as actual_systolic_bp, 24h_average_systolic_bp etc.

The naming is important in another way. Generally a subject variable should reflect a fact or assertion about the subject in reality rather than a purely epistemic view relating to an information system. For example a variable is_type1_diabetic is intended to reflect the patient’s real diabetic status, not just the knowledge of the local hospital EMR system of whether the patient is diabetic. Such variables may be termed 'ontic' i.e. reflecting the real world, rather than reflecting states of knowledge of some information source. The reason for using ontic variables is to allow DLM authors to define rules in terms of true clinical reality based on reliable previously established facts, rather than continually having to compensate for missing or unreliable knowledge within a guideline.

Epistemic variables may of course be defined, e.g. the variable has_diabetes_diagnosis directly reflects the idea that the presence of a diagnosis of a condition is distinct from the true fact of having the condition. These are typically used when the purpose of the guideline is to establish the presence or otherwise of the condition named in such variables.

3.8.1.2. Unavailable Values

One of the facilities created by a declaration of the form identifier: Type are subordinate predicate functions to detect if a value is available for the variable, i.e. if it is not logically null. Lack of a value is caused either by the true absence of the data in back-end systems (e.g. blood pressure has not recently been measured) or a technical failure to query or otherwise interrogate the relevant system.

It should be noted that within the overall conceptual model of process-based computing in openEHR, the common problem of a failure to locate a data item in back-end systems causing a live user to be asked to supply it is assumed to be addressed outside the DLM itself. This means that the simple lack of a value in back-end systems does not need to be compensated for by logic (such as null checks) in a DLM itself - it will already have been done in the Subject Proxy service. Consequently, if a variable value is unavailable within a DLM computation, this already takes into account attempts to obtain a value from a user.

The general case is that any subject variable might not have a value available for it, or at least a sufficiently current value (see next section for the concept of currency) at the moment of a particular rule invocation (remembering that the same rule might be invoked repeatedly over time). This means that the simple (primary variable) reference systolic_blood_pressure may in fact return a null value. If rules containing primary variable references such as systolic_blood_pressure are written under a non-null assumption, a null value will cause an exception of type no available value, and the original rule invocation will fail.

In most cases this is likely to be the preferred style of rule expression, since it makes rules simpler and clearer. However, in some cases, it may be known a priori that certain variables are only sometimes likely to be available, and if so, they are used, but if not, no exception is generated. This may be achieved by calling the subordinate predicate is_available as a guard on the direct access, as follows.

rules

    is_hypertensive:
        Result := systolic_blood_pressure.is_available and then systolic_blood_pressure.in_range([high]) or  ...

In the above, the semi-strict Boolean operator and then ensures the second reference to systolic_blood_pressure will only be evaluated if systolic_blood_pressure.is_available returns True.

3.8.2. Tracked Variables

An important category of subject variable is the tracked variable, which enables the current value of a variable, with some lag, to be obtained automatically from the back-end system. The acceptable lag is indicated by the subordinate currency attribute, as a temporal duration. This specifies how recent the value obtained from the external world (the 'sample') must be to be valid from the point of view of the DLM. Currency may be understood as the converse of 'staleness', that is, a variable sample that must be say 1 hour or less old is understood as stale after 1 hour.

The use of the currency modifier establishes that a subject variable is a time-related sample of some kind (instantaneous, average, minimum, etc) of a real-world time-varying continuant quality (e.g. blood pressure) of an independent continuant entity (usually a person).

Since the various physiological and disease process that occur in a human body have significantly differing temporal rhythms, currency will vary widely for different subject variables, as per the following examples.

input -- Administrative State

    |
    | untracked variable:
    | DOB never changes, no currency needed
    |
    date_of_birth: Date
        ;

input -- Historical State

    |
    | tracked variable:
    | weight changes over a period of days
    |
    weight: Quantity
        currency = 3 days
        ;

    |
    | untracked variable:
    | assuming an adult subject, height constant
    |
    height: Quantity
        ;

input -- Tracked State

    |
    | tracked variable:
    | blood glucose changes within minutes in response to food
    |
    blood_glucose: Quantity
        currency = 15 min
        ;

    |
    | tracked variable:
    | Heart-rate may change quickly
    |
    heart_rate: Quantity
        currency = 5 sec
        ;

Variables for which no currency is stated may be understood as having the currency equal to the age of the subject.

3.8.3. Quantitative Tracked Variables

Many tracked variables are quantitative, and a ubiquitous need within clinical guidelines and rules is to be able to refer to a continuous variable such as vital signs and most lab test values as being in a designated range. Such ranges may be the usual ones published e.g. the normal and high ranges for lipids in a cholesterol test for adults, or ranges defined by the DLM.

TBD: could ranges be declared elsewhere and re-used, e.g. standard BMI ranges, BP ranges etc?

The ranges for a subject variable are declared in the following way:

input -- Tracked State

    |
    | Systolic BP (mmHg)
    |
    systolic_blood_pressure: Real
        currency = 1 min
        ranges["mmHg"] =
            ------------------------------------
            | 180|:            [critical_high],
            |> 140|:            [very_high],
            |> 120|:            [high],
            |> 90 ..  120|:    [normal],
            | 90|:             [low],
            | 50|:             [critical_low]
            ------------------------------------
        ;

The above declaration allows the use of the predefined range function, which returns the most precise range in which the value falls, in rule expressions such as the following:

    Result :=
        case systolic_blood_pressure.range in
            =====================================
            [critical_high]:         [emergency],
            -------------------------------------
            [high], [very_high]:     [high_risk],
            -------------------------------------
            *:                       [monitor]
            =====================================
        ;

Note that ranges may be defined in two ways:

  • to be overlapping, such that [high] refers to any value higher than 120, while [very_high] refers to any value over 140;

  • non-overlapping, such that each sub-range stops at the start of the next one.

In the latter case, determining that a patient has high blood pressure requires an expression of the form systolic_blood_pressure.in_range ({[high], [very_high], [critical_high]}), using a set argument.

Sometimes there are multiple ranges, usually due to alternative units systems. This is handled by the use of a discriminator on a number of range groups. The following shows an example.

input -- Tracked State

    PaO2_FiO2_ratio: Quantity
        currency = 1 min,
        ranges["mm[Hg]"] =
            ---------------------------------
            |400 |:        [normal],
            |300 .. 399|:   [low],
            |200 .. 299|:   [very_low],
            |100 .. 199|:   [extremely_low],
            |<100|:         [critical_low]
            ---------------------------------
        ;,
        ranges["kPa"] =
            ---------------------------------
            |53|:          [normal],
            |39.9 .. 53|:   [low],
            |26.6 .. 39.8|: [very_low],
            |13.3 .. 26.5|: [extremely_low],
            |<13.3|:        [critical_low]
            ---------------------------------
        ;

To access a range within this structure, an expression of the form systolic_blood_pressure.in_range (["kPa"], [high]) is needed. This makes use of an overload of the in_range() function taking two keys. We can now see that the simpler form systolic_blood_pressure.in_range ([critical_high]) first chooses the first range group, and then extracts the required entry.

The above case reveals the general structure of the ranges property, with a formal type equivalent to Map <Terminology_code, Map <Interval <T>, Terminology_code>, where T is the declared type of the subject variable. In both the simple and discriminator cases, the entry |> 140|: [very_high] defines a [key, value] pair whose key is of type Interval <Real> and whose value is of type Terminology_code.

3.9. Rules Section

The DLM rules section is the section of primary importance, since it contains the rules for which a DLM is created. DLM rules are formally expressed as functions in the openEHR Expression Language, based on the openEHR BMM.

This section may be typically divided into two or more groups for authoring convenience. The first group may be used for simple Boolean-returning 0-order functions that represent 'named conditions', for use in the primary rules. The Boolean type may be omitted, since all conditions have this as their formal type. The following is an example.

rules -- Conditions

    her2_positive:
        Result := her2_expression = [positive]
        ;

    non_class_I_heart_failure:
        Result := has_heart_failure_class_II or
                    has_heart_failure_class_III or
                    has_heart_failure_class_IV
        ;

    anthracyclines_contraindicated:
        Result := has_transmural_MI or
            ejection_fraction.in_range ([low]) or
            non_class_I_heart_failure
        ;

The primary rules may be included in a separate rules group, consisting of 0-order functions returning any type. EL structures of any complexity may be used. The following provides an example.

rules -- Main

    hypertension_risk: Terminology_term
        Result :=
            choice in
                =================================================
                has_pre_eclampsia or
                has_eclampsia:                      [emergency],
                -------------------------------------------------
                previous_obstetric_hypertension or
                previous_pre_eclampsia or
                previous_eclampsia or
                has_pregnancy_hypertension:         [high_risk],
                -------------------------------------------------
                *:                                  [low_risk]
                =================================================
            ;

    gestational_diabetes_risk: Boolean
        Result :=
            bmi.in_range ([high]) or
            previous_macrosomic_baby or
            previous_gestational_diabetes or
            family_history_of_diabetes or
            race_related_diabetes_risk
        ;

3.10. Output Section

TBD:

3.11. Terminology Section

The definitions — Terminology section of a DLM serves the same purpose as the terminology section in an openEHR archetype, which is to provide multi-lingual definitions of all codes used in the artefact. Unlike archetypes, the codes in a DLM may be freely named, since they act as names of all symbolic entities referenced elsewhere in the DLM, including rules (i.e. functions), subject variables and constants. A typical DLM terminology section is shown below.

definitions -- Terminology

    terminology = {
        term_definitions: {
            "en" : {
                "date_of_birth" : {
                    text: "Date of birth"
                },
                "age_in_years" : {
                    text: "Age (years)"
                },
                "qRisk_score" : {
                    text: "QRISK2 score"
                },
                "diabetes_status" : {
                    text: "Diabetic status of subject"
                },
                "no_diabetes" : {
                    text: "Non-diabetic"
                },
                "type1_diabetes" : {
                    text: "Has type 1 diabetes"
                },
                "type2_diabetes" : {
                    text: "Has type 2 diabetes"
                }
            }
        }

        value_sets: {
            "diabetes_status" : {
                id: "diabetes_status",
                members: ["no_diabetes", "type1_diabetes", "type2_diabetes"]
            }
        }
    }
    ;

4. DLM Model

4.1. Overview

The structure of the proc.decision_language packages is shown below.

PROC decision language packages
Figure 1. proc.decision_language Package strucure

The packages are described below.

4.2. Decision Logic Module Package

The module structure of a Decision Logic Module is formally represented by a class model consisting of minimal extensions to the BMM class (i.e. BMM_CLASS) concept. A DLM is thus understood as a special kind of module expressible as a constrained form of class definition, such that only certain types of features are allowed. It also has additions for documentary meta-data and terminology provided by the AUTHORED_RESOURCE class from the openEHR BASE component.

The meta-model is shown below, with the meta-class DECISION_LOGIC_MODULE being the root entity.

PROC decision language.dlm
Figure 2. proc.decision_language.dlm Package

Within this model, the various DLM sections are modelled as specialisations of the BMM meta-class BMM_FEATURE_GROUP, so as to restrict the kind of features they may define. These are described in the following sub-sections.

4.2.1. Pre-conditions Section

The DLM preconditions section is formally represented as a feature group called 'preconditions' whose members are limited to being a single BMM_FUNCTION containing the Boolean expression that appears in that section. The expression may reference only rules and input variables.

4.2.2. Reference Section

The DLM reference section is formally represented as a feature group called 'reference' whose members are limited to BMM constants.

4.2.3. Input Section

The DLM input section is formally represented as a feature group called 'input' whose members are limited to BMM constants of a specific concrete type TRACKED_VARIABLE<T> and descendants, supplied by the proc.decision_language.concrete package.

4.2.4. Rules Section

The DLM rules section is formally represented as a feature group called 'rules' whose members are limited to BMM functions. A specific meta-type DLM_RULE is used in case extensions to the standard BMM_FUNCTION are required.

4.2.5. Class Descriptions

4.2.5.1. DECISION_LOGIC_MODULE Class

Class

DECISION_LOGIC_MODULE

Description

Specialised kind of class that represents a Decision Logic Module (DLM).

Inherit

AUTHORED_RESOURCE, BMM_SIMPLE_CLASS

Functions

Signature

Meaning

0..1

create_bmm_class

Generate a BMM_CLASS structure whose feature groups correspond to the formal sections of a DLM, instantiated using the DLM_XXX_SECTION classes.

4.2.5.2. DLM_REFERENCE_SECTION Class

Class

DLM_REFERENCE_SECTION

Description

Specialised feature group used in DLM to represent the reference section.

Inherit

BMM_FEATURE_GROUP

Attributes

Signature

Meaning

1..1
(redefined)

name: String
{default = "reference"}

Name of this feature group; defaults to 'reference'.

0..1
(redefined)

features: List<BMM_CONSTANT>

Constant definitions needed by module.

4.2.5.3. DLM_INPUT_SECTION Class

Class

DLM_INPUT_SECTION

Description

Specialised feature group used in DLM to represent the input section. Contains only SUBJECT_TRACKED_VARIABLE members.

Inherit

BMM_FEATURE_GROUP

Attributes

Signature

Meaning

1..1
(redefined)

name: String
{default = "input"}

Name of this feature group; defaults to 'input'.

0..1
(redefined)

features: List<BMM_CONSTANT>

Input variable proxies.

4.2.5.4. DLM_RULES_SECTION Class

Class

DLM_RULES_SECTION

Description

Specialised feature group used in DLM to represent the rules section. Contains only DLM_RULE members.

Inherit

BMM_FEATURE_GROUP

Attributes

Signature

Meaning

1..1
(redefined)

name: String
{default = "rules"}

Name of this feature group; defaults to 'rules'.

0..1
(redefined)

features: List<DLM_RULE>

Rules for this module.

4.2.5.5. DLM_RULE Class

Class

DLM_RULE

Description

Specialised form of BMM_FUNCTION that acts as a DLM rule.

Inherit

BMM_FUNCTION

4.3. DLM Concrete Model

In order to supply a few of the key constructs of a DLM, a concrete model is provided with specific types that express the needed semantics. The main types required relate to input variables. The DLM concrete model UML is shown below.

PROC decision language.concrete
Figure 3. proc.decision_language.concrete Package

4.3.1. Subject Variables

The variable definitions found in the input section of a DLM are designed to facilitate both intuitive DLM authoring and tracking of values over time from real data sources, typically health records, devices and so on. The generic class STATE_VARIABLE<T> and descendants provide a representation of a subject variable with a symbolic name. The generic type parameter is substituted with types used for representing atomic data values, including primitive types such as String, Integer etc, as well as types like Terminology_code, Quantity etc, as available in the models (type definitions) used by a DLM.

The class STATE_VARIABLE<T> models the simplest case: a variable that may be queried once for an execution run. This is sufficient for subject variables that are not time-dependent, and will not change during the execution, such as date_of_birth, sex, and Boolean variables such as is_type1_diabetic. The history attribute contains the variable value (normally only one sample), while the features is_available() and unavailable_reason() provide a way for missing data to be checked for.

For time-varying variables, the values must usually be tracked. The class TRACKED_STATE_VARIABLE<T> is is provided for this purpose, and adds the meta-attribute currency, a duration indicating the maximum age of the sample value to be useful or valid. The history attribute now contains variable samples over time. The function value() extracts the most recent available value, while is_available() is redefined to indicate whether a sufficiently recent value is available.

Many tracked variables are quantitative, and for these, extra facilities are provided via the class TRACKED_QUANTITATIVE_VARIABLE<T>, which adds the ranges facility described earlier in Section 3.8.3.

Variable declarations in DL syntax, as described in the language section above are in fact instantiations of the STATE_VARIABLE<T> or a descendant, potentially with constructor paramaters supplied. Consider the following declaration, shown earlier:

    systolic_blood_pressure: Quantity
        currency = 1 min,
        ranges["mm[Hg]"] =
            --------------------------------
            |180|:         [critical_high],
            |>140|:         [very_high],
            |>120|:         [high],
            |>90..120|:    [normal],
            |90|:          [low],
            |50|:          [critical_low]
            --------------------------------
        ;

If we were to rewrite this in a more standard programming language, it might look like the following object instantiation, preceded by setup code (the syntax 'Type_name (args)' is used to represent an object creation using a constructor call in each case):

// build the ranges Map
ranges: Map <Terminology_code, Interval<Quantity> ();
ranges.put (Terminology_code("critical_high"), Interval<Quantity>(180 mm[Hg], Void);
    ...
ranges.put (Terminology_code("critical_low"), Interval<Quantity>(Void, 50 mm[Hg]);

// instantiate the tracked variable object
systolic_blood_pressure: TRACKED_QUANTITATIVE_VARIABLE<Quantity> (
    "systolic_blood_pressure",   // name
    "PT1M",                      // currency
    ranges                       // ranges
);

It will be appreciated that this is substantially more complex, and also arcane to a domain specialist. In the above, the part between the parentheses in the lower declaration corresponds to a call to a constructor of type TRACKED_QUANTITATIVE_VARIABLE with the signature (name: String, currency: Iso8601_duration, ranges: Map<Terminology_code, Interval<T>). From a formal point of view, such an instantiation creates an instance that will remain for the life of the DLM’s execution, in other words, a computed constant value. It is thus a meta-instance of the BMM meta-type BMM_CONSTANT, as shown in the DLM model above.

Each input variable declaration creates an object similar to the above, which may then be used during the execution of the DLM by calls made from the functions in the rules section. The simplest possible 'bare' reference of the variable name alone, i.e. systolic_blood_pressure in the example above, makes use of the default delegate feature of the BMM (BMM_FUNCTION) to invoke the value() function, avoiding the need to write systolic_blood_pressure.value(). However, all the features defined on TRACKED_VARIABLE and its descendants are indeed available for use in DLM code if needed. Thus, systolic_blood_pressure.in_range([high]) is a normal call on the systolic_blood_pressure instance of TRACKED_QUANTITATIVE_VARIABLE<Quantity>.

TO BE CONTINUED

4.3.2. Class Descriptions

4.3.2.1. STATE_VARIABLE Class

Class

STATE_VARIABLE<T>

Description

Abstraction of a real-world variable that represents a quality of a subject, e.g. patient diabetic status, date of birth etc.

The generic parameter is a model type from an accessible model.

Attributes

Signature

Meaning

0..1

history: Hash<String,VALUE_SNAPSHOT>

Local history of values.

1..1

name: String

Name of variable.

Functions

Signature

Meaning

1..1

is_available (): Boolean

Return True if there is a value in history.

0..1

unavailable_reason (): String

If is_available is False, potentially provide a reason.

0..1

value (): T

Retrieve latest value, or null if none.

4.3.2.2. VALUE_SNAPSHOT Class

Class

VALUE_SNAPSHOT<T>

Description

One or more snapshot attempts of the state variable, either containing values or unavailable reason.

Attributes

Signature

Meaning

0..1

value: T

Value of variable in the real world, if available; Void if not.

0..1

effective_time: Iso8601_date_time

Real-world time at which value was last True, if available.

1..1

sample_time: Iso8601_date_time

Time at which this sample was obtained from its source (e.g. a patient record system, device, or user interaction).

0..1

unavailable_reason: String

Functions

Signature

Meaning

1..1

is_available (): Boolean

Return True if value is not Void.

Invariants

Inv_available: is_available() implies value /= Void

4.3.2.3. TRACKED_STATE_VARIABLE Class

Class

TRACKED_STATE_VARIABLE<T>

Description

A 'tracked' subject variable representing a continuant quality of a subject over time, e.g. patient systolic blood pressure, heart rate etc, whose value is sampled over time.

The generic parameter is a model type from an accessible model.

Inherit

STATE_VARIABLE

Attributes

Signature

Meaning

0..1

currency: Iso8601_duration

If set, maximum age of a real-world snapshot of this variable’s value to be usable or valid.

0..1

max_history: Iso8601_duration

Functions

Signature

Meaning

1..1

value_at_time (
a_time: Iso8601_date_time[1]
): T

Return the value from history that was available at time time.

1..1
(redefined)

is_available (): Boolean

Return True if there is a member of history whose time is within the duration of currency of the current moment.

4.3.2.4. TRACKED_QUANTITATIVE_VARIABLE Class

Class

TRACKED_QUANTITATIVE_VARIABLE<T>

Description

Specialised property that represents a 'tracked variable' within the DLM input section. A tracked variable is used to record snapshots of an external variable such as a subject blood pressure.

Inherit

TRACKED_STATE_VARIABLE

Attributes

Signature

Meaning

0..1

unit_ranges: Hash<String,EL_CASE_TABLE<EL_LITERAL>>

Range definitions, represented as tables, each one exhaustively covering the value space.

Functions

Signature

Meaning

1..1

in_range (
a_range: Terminology_code[1]
): Boolean

Determine if the last read value of the variable is in the named range within the default group. Use when there is only one range group.

1..1

range (): Terminology_code

Return the name of the range from the default range group within which the last read value of the variable resides. Use when there is only one range group.

1..1

range_definition (
a_range: Terminology_code[1]
): Interval<T>

Return the interval of the named range within the default range group. Use when there is only one range group.

1..1

in_group_range (
range_group: Terminology_code[1],
a_range: Terminology_code[1]
): Boolean

Form of in_range() to use when there is more than one range group.

1..1

group_range (
range_group: Terminology_code[1]
): Terminology_code

Form of range() to use when there is more than one range group.

1..1

group_range_definition (
range_group: Terminology_code[1],
a_range: Terminology_code[1]
): Interval<T>

Form of range_def() to use when there is more than one range group.

References