3.10. Interpretation of map_variables elements

  1. For the purposes of this specification, the conceptual “variable equivalence network” SHALL be an undirected graph with one node for every variable element in the CellML model.

    1. The arcs of this graph SHALL be equivalences defined in the CellML model.

See more

Understanding the terminology

The idea of component modularity and separateness is helpful in organising models and reusing parts of them, but only if it’s also possible to share information between the distinct components. This is the goal of “variable equivalence”, and requires the combined mechanisms of connection elements (to connect components) and map_variables elements (to connect their variables). Several different words have been used to describe what is essentially this one process: equivalent variables, the connected variable set, mapped variables, etc. All of these mean the same thing: that the variables in the set act as a single agent throughout the model. They may have different local names within a component, but they will have the same value everywhere they exist.

Thus there are two ways in which variables are related to one another. The first is through the mathematical equations as specified in intra-component math elements; the second is through variable equivalences as specified in inter-component connection and map_variables elements.

Both the connection and map_variables elements are undirected. There is no update direction or hierarchy; they all represent, instantaneously, the same thing.

  1. For each map_variables element present in the CellML model, we define variables A and B for use in the rules in this section as follows:

    1. Variable A SHALL be the variable referenced by the encompassing connection element’s component_1 and this map_variables element’s variable_1 attribute; and

    2. Variable B SHALL be the variable referenced by the encompassing connection element’s component_2 and this map_variables element’s variable_2 attribute.

  2. For every map_variables element present in the CellML model, there SHALL be an arc in the variable equivalence network.

    1. One endpoint of the arc in the variable equivalence network SHALL be the node corresponding to variable A; and

    2. One endpoint of the arc in the variable equivalence network SHALL be the node corresponding to variable B.

See more

Understanding the players

You can think of the previous two points as defining an address using a street name and number. Since streets (components) have lots of houses (variables) they all need different numbers (variable names) in that street. And since lots of streets (components) have houses (variables) with the same number (name), we need both the street name and the house number in order to locate a house properly. This is the purpose of the component_1 and component_2 attributes of connection elements (to define the respective street (component) names), and the variable_1 and variable_2 attributes of each connection element’s map_variables element (to define the respective house number (variable name)).

Thus the “equivalent variable network” is a collection of unique variables (each specified a component and a variable name), which will be treated as the same variable throughout the model.

Note that we use the term “network” here as it best describes the process by which variable equivalence is constructed, even if its final interpretation is not a network, but rather a set.

  1. CellML models MUST NOT contain any pair of map_variables elements which duplicates an existing arc in the variable equivalence network.

  2. The variable equivalence network MUST NOT contain any cycles.

See more

Understanding the rules

As well as the syntax definitions above, there are rules regarding which variables are able to be connected to one another too.

Both points 3.10.4 and 3.10.5 address a similar issue: that the “network” of variable equivalence must not contain any cycles (a connection such that A-B-A is just a mini cycle after all).

In the model below there are three sets of variables which have equivalent values, and it is the modeller’s desire that they be able to use them interchangably in each language whilst maintaining their value throughout. There are two situations which would render the model invalid if these variables were mapped incorrectly, as shown below.

model: LearningToCount
  ├─ component: Dutch
  │   ├─ variable: een
  │   ├─ variable: twee
  │   ├─ variable: drie
  │   └─ variable: vier
  ├─ component: Maori
  │   ├─ variable: tahi
  │   ├─ variable: rua
  │   ├─ variable: toru
  │   └─ variable: wha
  └─ component: French
      ├─ variable: un
      ├─ variable: deux
      ├─ variable: trois
      └─ variable: quatre

See CellML syntax

<model name="LearningToCount">
  <component name="Dutch">
    <variable name="een" />
    <variable name="twee" />
    <variable name="drie" />
    <variable name="vier" />
  </component>
  <component name="Maori">
    <variable name="tahi" />
    <variable name="rua" />
    <variable name="toru" />
    <variable name="wha" />
  </component>
  <component name="French">
    <variable name="un" />
    <variable name="deux" />
    <variable name="trois" />
    <variable name="quatre" />
  </component>
</model>
  1. Firstly, duplicated connections are not permitted because they create a mini-cycle of two variables. If an Anglophone is given a definition of een to mean tahi, and tahi to mean een, they are none the wiser.

model: LearningToCount
  ├─ component: Dutch
  │   ├─ variable: een <╴╴╴┬╴╴╴┐
  │   ├─ variable: twee    ╷   ╷
  │   ├─ variable: drie   duplicate mapping
  │   └─ variable: vier   causes a mini-cycle
  ├─ component: Maori      ╵   ╵
  │   ├─ variable: tahi <╴╴┴╴╴╴┘
  │   ├─ variable: rua
  │   ├─ variable: toru
  │   └─ variable: wha
  └─ component: French
      ├─ variable: un
      ├─ variable: deux
      ├─ variable: trois
      └─ variable: quatre

See CellML syntax

<!-- Valid: First define the two components using a connection ... -->
<connection component_1="Dutch" component_2="Maori">
  <!-- ... and then define the mapping between two variables within
      those two components. -->
  <map_variables variable_1="een" variable_2="tahi" />
    ...
</connection>

<!-- Invalid: Duplicated mappings are not allowed. -->
<connection component_1="Dutch" component_2="Maori">
  <map_variables variable_1="een" variable_2="tahi" />
  <map_variables variable_1="een" variable_2="tahi" />
   ...
</connection>

<!-- Invalid: Duplicated connections are not allowed. -->
<connection component_1="Dutch" component_2="Maori">
  <map_variables variable_1="een" variable_2="tahi" />
    ...
</connection>
<connection component_1="Maori" component_2="Dutch">
  <map_variables variable_1="tahi" variable_2="een" />
    ...
</connection>
  1. Secondly, any form of cyclical definition is invalid, as it leaves the mathematical model underdefined. So our Anglophone could be also told that drie means trois, trois means toru, and toru means drie, but unless one of them is nailed down to an actual value somewhere, the model remains under-defined.

model: LearningToCount
  ├─ component: Dutch
  │   ├─ variable: een <╴╴╴╴┐<╴╴╴╴╴╴╴╴┐
  │   ├─ variable: twee     ╷         ╷
  │   ├─ variable: drie    cycle created
  │   └─ variable: vier     ╷         ╷
  ├─ component: Maori       ╷         ╷
  │   ├─ variable: tahi <╴╴╴┘<╴╴╴┐    ╷
  │   ├─ variable: rua           ╷    ╷
  │   ├─ variable: toru          ╷    ╷
  │   └─ variable: wha           ╷    ╷
  └─ component: French           ╷    ╷
      ├─ variable: un <╴╴╴╴╴╴╴╴╴╴┘<╴╴╴┘
      ├─ variable: deux
      ├─ variable: trois
      └─ variable: quatre

See CellML syntax

<!-- Invalid: a cycle is created. -->
<connection component_1="Dutch" component_2="Maori">
  <map_variables variable_1="een" variable_2="tahi" />
    ...
</connection>
<connection component_1="Maori" component_2="French">
  <map_variables variable_1="tahi" variable_2="un" />
    ...
</connection>
<connection component_1="French" component_2="Dutch">
  <map_variables variable_1="un" variable_2="een" />
    ...
</connection>
  1. For a given variable, the “available interfaces” SHALL be determined by the value of the interface attribute on the corresponding variable element as follows:

    1. A value of public specifies that the variable has a public interface;

    2. A value of private specifies that the variable has a private interface;

    3. A value of public_and_private specifies that the variable has both a public and a private interface;

    4. A value of none specifies that the variable has no interface; or

    5. If the interface attribute is absent, then the variable has no interface.

  2. The “applicable interfaces” for variables A and B in components AA and BB respectively SHALL be defined as follows:

    1. When component AA is in the sibling set of component BB (and vice versa), the applicable interface for both variables A and B SHALL be the public interface.

    2. When component AA is in the encapsulated set of component BB, the applicable interface for variable A SHALL be the public interface, and the applicable interface for variable B SHALL be the private interface.

    3. When component BB is in the encapsulated set of component AA, the applicable interface for variable B SHALL be the public interface, and the applicable interface for variable A SHALL be the private interface.

    Note

    For the avoidance of doubt, if components AA and BB are in each other’s hidden set, then there are no applicable interfaces for the variables A and B.

  1. CellML models MUST only contain map_variables elements where the applicable interfaces for both variables are available.

See more

Understanding mappings and units

The only place in which valid units items can cause a model to be invalid is between mapped variables. Point 3.10.6 means that each variable within a map_variables element must have an equivalent unit reduction. Please see that section for more details.

  1. For each map_variables element present in the CellML model, the variable unit reduction (see 3.7 Effect of units on a variable) of variable A MUST have an identical set of tuples to the variable unit reduction of variable B.

    1. Two sets of tuples SHALL be considered identical if all of the tuples from each set are present in the other, or if both sets are empty.

    2. Two tuples are considered identical if and only if both the unit name and exponent of each tuple are identical.

  2. Tuples differing by a multiplying factor in their unit reduction MUST be taken into account when interpreting the numerical values of the variables (see 3.3 Interpretation of units elements).

See more

Understanding interfaces

There are two hurdles to get past before variables can be mapped to one another. The first is the relative position within the encapsulation hierarchy of the variable elements’ parent component elements (as discussed in 3.9 Interpretation of encapsulation elements, and the second is the value of the interface_type attribute given to each variable itself. This means that even if two components are sufficiently close relatives that they can be connected, the modeller still has control at the individual variable level as to which of those relatives can have access.

The interface_type attribute value choices are: - public (used by child components to reference their parent, or between siblings), - private (used by parent components to reference their children), - public_and_private (used when both the public and private interfaces are required), or - none, which is the default.

Which one(s) of these are deemed applicable for any variable mapping is determined by the relationship between the their parent component elements.

In the example below, Christopher Robin must assemble an average score of the mood of all of his friends in the Hundred Acre Wood. He does not have access to all of their mood scores directly, so must use mapped variables in order to create the average.

model: TheHouseAtPoohCorner
  ├─ component: ChristopherRobin
  │   ├─ variable: my_mood
  │   ├─ variable: average_mood_of_everyone
  │   ├─ variable: roos_mood
  │   ├─ variable: kangas_mood <╴╴╴╴╴┐
  │   ├─ variable: eeyores_mood <╴╴╴┐╷
  │   └─ variable: poohs_mood <╴╴╴╴┐╷╷
  │                                ╷╷╷
  │                     connections between these
  │            components are possible, depending
  │                  on the variables' interfaces
  │                                ╵╵╵
  ├─ component: WinnieThePooh      ╵╵╵
  │   └─ variable: mood ╴╴╴╴╴╴╴╴╴╴╴┘╵╵
  ├─ component: Eeyore              ╵╵
  │   └─ variable: mood ╴╴╴╴╴╴╴╴╴╴╴╴┘╵
  └─ component: Kanga                ╵
      │  ├─ variable: mood ╴╴╴╴╴╴╴╴╴╴┘
      └─ component: Roo
          └─ variable: mood ╴╴╴╴╴╴╴╴╴╴> This variable cannot be connected to
                                        anything within the ChristopherRobin
                                        component as its component is too distant.

At present, the connections above in dashed lines are possible, as long as the variables have interface types which support them. One connection is not possible (yet): Christopher Robin doesn’t know Roo’s mood, because their components are too distant (grandparent/grandchild) to be directly connected. We can add a utility variable roos_mood to the Kanga component in order to pass along its value to the ChristopherRobin component. We’ll also add interface types to the variables, and discuss their effects below.

model: TheHouseAtPoohCorner
  ├─ component: ChristopherRobin
  │   ├─ variable: my_mood
  │   ├─ variable: average_mood_of_everyone
  │   ├─ variable: roos_mood (interface: public) <╴╴╴╴╴ D ╴╴╴╴╴╴╴╴╴╴╴╴┐
  │   ├─ variable: kangas_mood (interface: none) <╴╴╴╴╴ C ╴╴╴╴┐       ╷
  │   ├─ variable: eeyores_mood (interface: public) <╴╴ B ╴╴╴┐╷       ╷
  │   └─ variable: poohs_mood (interface: public) <╴╴╴╴ A ╴╴┐╷╷       ╷
  │                                                         ╷╷╷  utility variable added so
  ├─ component: WinnieThePooh                               ╷╷╷  that Roo's mood can be passed
  │   └─ variable: mood (interface: public)  ╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴┘╷╷  down to Christopher Robin
  ├─ component: Eeyore                                       ╷╷       ╵
  │   └─ variable: mood ╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴┘╷       ╵
  └─ component: Kanga                                         ╷       ╵
      │  ├─ variable: mood (interface: public) ╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴┘       ╵
      │  └─ variable: roos_mood (interface: private) <╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴┤
      └─ component: Roo                                               ╷
          └─ variable: mood (interface: public) ╴╴╴╴╴╴ E ╴╴╴╴╴╴╴╴╴╴╴╴╴┘

See CellML syntax

<model name="TheHouseAtPoohCorner">

  <!-- Defining the components and local variables. -->
  <component name="ChristopherRobin">
    <variable name="my_mood" interface_type="none" units="mood_score" />
    <variable name="average_mood_of_everyone" interface_type="none" units="mood_score" />
    <variable name="roos_mood" interface_type="public" units="mood_score" />
    <variable name="kangas_mood" interface_type="none" units="mood_score" />
    <variable name="eeyores_mood" interface_type="public" units="mood_score" />
    <variable name="poohs_mood" interface_type="public" units="mood_score" />
  </component>

  <component name="WinnieThePooh">
    <variable name="mood" interface_type="public" units="mood_score" />
  </component>

  <component name="Eeyore">
    <variable name="mood" units="mood_score" />
  </component>

  <component name="Kanga">
    <variable name="mood" interface_type="public" units="mood_score" />
    <!-- The utility variable roos_mood is included here so that it can pass
         the value of mood in component Roo to roos_mood in component ChristopherRobin. -->
    <variable name="roos_mood" interface_type="private" units="mood_score" />
  </component>

  <component name="Roo">
    <variable name="mood" interface_type="public" units="mood_score" />
  </component>

  <!-- Defining connections and mapped variables. -->
  <connection component_1="ChristopherRobin" component_2="WinnieThePooh">
    <!-- Mapping A -->
    <map_variables variable_1="poohs_mood" variable_2="mood" />
  </connection>

  <connection component_1="ChristopherRobin" component_2="Eeyore">
    <!-- Mapping B -->
    <map_variables variable_1="eeyores_mood" variable_2="mood" />
  </connection>

  <connection component_1="ChristopherRobin" component_2="Kanga">
    <!-- Mapping C -->
    <map_variables variable_1="kangas_mood" variable_2="mood" />
    <!-- Mapping D -->
    <map_variables variable_1="roos_mood" variable_2="roos_mood" />
  </connection>

  <connection component_1="Kanga" component_2="Roo">
    <!-- Mapping E -->
    <map_variables variable_1="roos_mood" variable_2="mood" />
  </connection>

</model>
  • Mapping A is valid. The sibling components WinnieThePooh and ChristopherRobin have public interfaces between their mood and poohs_mood variables respectively. This follows point 3.10.9.1 and is valid.

  • Mapping B is not valid. By default an interface type is none, and since the mood variable in component Eeyore does not specify one, no mappings are permitted. For that connection to exist, the mood variable must have an interface type public.

  • Mapping C is not valid. The variable kangas_mood has explicitly specified that no mappings are possible by using the none interface type. For this mapping to be valid, the type needs to be public.

  • Mapping D is not valid. Because they are sibling components, the variables in ChristopherRobin and Kanga must both have the interface type of public in order to be valid … but there’s a twist.

  • Mapping E is currently valid, because variables in component Kanga can only access variables in its child component Roo with a private interface. But if Mapping D is to be made valid, that same variable must maintain a public interface in order to access variables in its sibling component ChristopherRobin. It is for this reason that the public_and_private interface type exists. For Mappings D and E to be valid, the variable roos_mood in component Kanga must have an interface type of public_and_private.

The corrected model is shown below.

model: TheHouseAtPoohCorner
  ├─ component: ChristopherRobin
  │   ├─ variable: my_mood (interface: none)
  │   ├─ variable: average_mood_of_everyone (interface: none)
  │   ├─ variable: roos_mood (interface: public) <╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴┐
  │   ├─ variable: kangas_mood (interface: public) <╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴┐╷
  │   ├─ variable: eeyores_mood (interface: public) <╴╴╴╴╴╴╴╴╴╴╴╴╴┐╷╷
  │   └─ variable: poohs_mood (interface: public) <╴╴╴╴╴╴╴╴╴╴╴╴╴╴┐╷╷╷
  │                                                              ╷╷╷╷
  ├─ component: WinnieThePooh                                    ╷╷╷╷
  │   └─ variable: mood (interface: public) ╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴┘╷╷╷
  ├─ component: Eeyore                                            ╷╷╷
  │   └─ variable: mood (interface: public) ╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴┘╷╷
  └─ component: Kanga                                              ╷╷
      │  ├─ variable: mood (interface: public) ╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴ ╴╴╴╴┘╷
      │  └─ variable: roos_mood (interface: public_and_private) <╴╴╴┤
      └─ component: Roo                                             ╷
          └─ variable: mood (interface: public) ╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴┘

See CellML syntax

<model name="TheHouseAtPoohCorner">

  <!-- Defining the components and local variables. -->
  <component name="ChristopherRobin">
    <variable name="my_mood" interface_type="none" units="mood_score" />
    <variable name="average_mood_of_everyone" interface_type="none" units="mood_score" />
    <variable name="roos_mood" interface_type="public" units="mood_score" />
    <variable name="kangas_mood" interface_type="public" units="mood_score" />
    <variable name="eeyores_mood" interface_type="public" units="mood_score" />
    <variable name="poohs_mood" interface_type="public" units="mood_score" />
  </component>

  <component name="WinnieThePooh">
    <variable name="mood" interface_type="public" units="mood_score" />
  </component>

  <component name="Eeyore">
    <variable name="mood" interface_type="public" units="mood_score" />
  </component>

  <component name="Kanga">
    <variable name="mood" interface_type="public" units="mood_score" />
    <!-- The utility variable roos_mood is included here so that it can pass
         the value of mood in component Roo to roos_mood in component ChristopherRobin. -->
    <variable name="roos_mood" interface_type="public_and_private" units="mood_score" />
  </component>

  <component name="Roo">
    <variable name="mood" interface_type="public" units="mood_score" />
  </component>

  <!-- Defining connections and mapped variables. -->
  <connection component_1="ChristopherRobin" component_2="WinnieThePooh">
    <map_variables variable_1="poohs_mood" variable_2="mood" />
  </connection>

  <connection component_1="ChristopherRobin" component_2="Kanga">
    <map_variables variable_1="kangas_mood" variable_2="mood" />
    <map_variables variable_1="roos_mood" variable_2="roos_mood" />
  </connection>

  <connection component_1="ChristopherRobin" component_2="Eeyore">
    <map_variables variable_1="eeyores_mood" variable_2="mood" />
  </connection>

  <connection component_1="Kanga" component_2="Roo">
    <map_variables variable_1="roos_mood" variable_2="mood" />
  </connection>

</model>
  1. The variable elements in a CellML model SHALL be treated as belonging to a single “equivalent variable set”.

    1. Each set of equivalent variables is the set of all variable elements for which the corresponding nodes in the variable equivalence network form a connected subgraph.

    2. Each set of equivalent variables represents one variable in the underlying mathematical model.

See more

Understanding mathematics

In an episode of Bill Watterson’s comic, Calvin and Hobbes, Calvin builds a duplicator machine and clones himself several times so that the clones can do his chores in different rooms of the house. This is essentially the purpose of creating map_variables elements: to enable the “clones” (the mapped variables) to be used in separate “rooms” (components) whilst really representing the same thing. The first sentence in 3.10.11 explains this: Each variable element in a CellML model SHALL be treated as belonging to a single “connected variable set”; or, while there are many “connected variable sets” in a model, any one variable may belong to only one of them.

In a CellML model there are two mechanisms by which variables can relate to one another: through the mathematics in the math elements, and through the mapping in the map_variables elements. In a mathematical model, there is only one mechanism: the maths itself. There are no clones, no duplicates, no mappings.

Thus, the purely mathematical model is found by collapsing each set of mapped variables (cloned characters) onto a single unique variable (character), until the relationships between these unique variables no longer involve mapping (cloning) but only mathematics. In the end, Calvin does the same, and the world returns to (roughly) normal.