Version: [release notes]

Module 2.7: Constants and algebraic variables

So far, in our models, we have only used discrete variables. Discrete variables hold a value, which can be used in for instance guards, and can be updated by assignments on edges. We will now introduce constants, that hold a fixed value, and algebraic variables, that hold a value that is computed from other values, such as those of constants and discrete variables. We will introduce these new concepts through an example.

Example

Consider the following model of a bridge, with a bridge deck, a bridge light and a car:

                        
                            automaton bridge_deck:
                                disc int angle = 0;
                                event open_further, close_further;

                                const int step = 10;

                                alg bool is_open   = angle >= 90;
                                alg bool is_closed = angle <= 0;

                                location:
                                    initial;
                                    edge open_further  when angle < 90 do angle := angle + step;
                                    edge close_further when angle >  0 do angle := angle - step;
                            end

                            automaton bridge_light:
                                event turn_on, turn_off;

                                alg bool is_on = On;
                                alg bool is_off = Off;

                                location On:
                                    initial;
                                    edge turn_off goto Off;

                                location Off:
                                    edge turn_on goto On;
                            end

                            alg bool car_may_drive     =     bridge_deck.is_closed and bridge_light.is_off;
                            alg bool car_may_not_drive = not bridge_deck.is_closed or  bridge_light.is_on;
                        
                    

The bridge_deck automaton models the bridge deck. The bridge deck can open or close. If the deck is closed, and thus horizontal, cars can drive over it. As it opens, its angle compared to the road increases from 0 to 90, and cars can not drive over it. Since the automaton has only a single location, its name is left out. There are two edges, one to open the deck a bit further, and one to close it a bit further. Each occurrence of one of these events opens or closes the bridge deck a bit, changing its angle by a number of degrees. Since opening and closing the deck is done in increments of ten degrees, a constant step is defined to hold this value.

Constants are declared using the const keyword. Like variables, constants have a name, a type and a value. In this case, a constant named step is defined of type int and with value 10. Like variables, constants can also have a different type, such bool or real. Constants hold a fixed value that can not be changed by assignments. Still, constants can be useful. By giving a value a name, the name can be used instead of the value. This can make models more readable, as you don't have to guess what the value represents. Furthermore, if the constant is used in multiple places, then changing the value of the constant has an affect in all places where it is used. This makes it easier to change values that are used multiple times in the model, and thus to keep the model consistent after such changes.

The bridge deck is open at 90 degrees and closed at 0 degrees. The bridge deck automaton has two algebraic variables is_open and is_closed, that model sensors that indicate whether the bridge deck is open or closed, respectively. An algebraic variable are declared using the alg keyword and have a name, a type and a defining expression. The expression represents a computation that produces the value of the algebraic variable, and this computation may make use of constants and other variables. For instance, algebraic variable is_open has a bool type. Its value is thus either true or false. Its defining expression is angle >= 90, which indicates that if the bridge deck's angle is at least 90 degrees, then the bridge deck is open. The angle refers to the discrete variable named angle. The value of the algebraic variable is_open thus depends on the value of the discrete variable angle. If angle is at least 90, then is_open is true, and otherwise, if angle is less than 90, then is_open is false. The algebraic variable can't be assigned a new value. Instead, its value is at all times determined by its defining expression. But, assigning a different value to discrete variable angle may lead to is_open getting a different value as well. That is, if angle is 80 and the open_further edge is taken, then angle becomes 90 and the value of is_open automatically changes from false to true. Similarly, the value of algebraic variable is_closed also changes based on the value of discrete variable angle, and is only true if the angle is at most zero.

If the values of the sensors would instead be modeled as discrete variables, they would need to be updated on every edge where the value of variable angle is updated. That is, if the angle is increased, is_open would need to be assigned value false if the angle was 0 before the edge is taken, and is_closed would need to be assigned value true if the angle becomes 90 after the edge is taken. And similarly for the edge where the angle is decreased. A benefit of algebraic variables is that they don't have to be updated on every edge. Instead, they are defined once and automatically change value as needed. This benefit increases when there are more edges where the variable would have otherwise been needed to be updated. And when a new edge is added to the automaton that also updates the angle, the algebraic variable does not need to be changed. If the variables would instead be modeled as discrete variables, the new edge would also need to update them. If this would be forgotten, the variables would no longer hold the right value. Algebraic variables can thus reduce modeling effort and improve consistency.

The bridge_light automaton models the bridge light. When the light is on, cars may not drive over the bridge. Cars may only drive over the bridge when the light is off. This automaton also has two algebraic variables, is_on and is_off, that indicate whether the light is on or off, respectively. The is_on is true when the automaton is in its On location, and is false otherwise. Similarly, is_off is defined by the automaton being in its Off location.

The car is modeled by two algebraic variables that model the whether it may or may not drive over the bridge. The car may only drive over the bridge if the bridge deck is closed, and the bridge light is off. Both conditions must hold. This is the case when the algebraic variable is_closed of the bridge_deck automaton and the algebraic variable is_off of the bridge_light automaton are both true. Hence, car_may_drive is defined by expression bridge_deck.is_closed and bridge_light.is_off. In all other situations, thus when the bridge deck is not closed, or the bridge light is off, or both, the car may not drive. Hence, car_may_not_drive is defined by expression bridge_deck.is_open or bridge_light.is_on. Similar to discrete variables, algebraic variables can be read globally, and if defined inside an automaton, and read outside of it, their name must be prefixed with the name of the defining automaton and a period. Note that instead of the boolean algebraic variable bridge_light.is_on, also the On location of the bridge_light could have been used directly. That is, the defining expression of car_may_not_drive could instead have also been defined by expression bridge_deck.is_open or bridge_light.On.

In general, algebraic variables can be used to give names to expressions (computations), similar to how constants can be used to give names to fixed values. This brings similar benefits, in terms of readability and making it easier to change the model consistently. Boolean algebraic variables can often be used instead of locations, or vice versa. For instance, the above model could also be modeled using only locations and no algebraic variables. However, using algebraic variables can often make it easier to read and work with the model.

Besides that, algebraic variables offer a means of abstraction. The car can refer to the algebraic variables like is_closed. It does not have to know how this algebraic variable is defined. It does not have to that the bridge deck has an angle, nor at which exact angle the it is closed. As the bridge deck automaton is changed, and even the defining expression of is_closed, this does not matter to the car. It can still use the algebraic variable as before, assuming its name and type have not changed. Algebraic variables thus offer a form of abstraction, making it easier to adapt the part of the model that it abstracts.

Quiz 1

[ { type: 'single-choice', question: ` The above example is also part of the course files (folder module2, file constants-and-algebraic-variables.cif). Simulate it and check with the state visualizer whether the model, and especially the algebraic variables, show the desired behavior. What do you think?`, answers: [ "Yes, the model has the desired behavior.", "No, the model does not have the desired behavior." ], correctAnswer: '1' } ]

Exercise 1

Check the above model from the above quiz for unreachable and blocking states. Does the result of the trim check make sense? If not, why not, and how can you fix it?

The trim check indicates that all locations are blocking. This is to be expected, since none of the locations are marked. Therefore, no location can reach a marked location. To resolve this for this example, the initial locations can be marked.

Quiz 2

[ { type: 'single-choice', question: "How can the value of an algebraic variable be changed?", answers: [ "By assigning it a new value in an update.", "The value changes automatically according to the expression of the variable.", "By assigning it a new value in an expression." ], correctAnswer: '2' }, { type: 'single-choice', question: "Can an algebraic variable be read by another algebraic variable in another automaton?", answers: [ "Yes, that is possible because of the 'global read' concept.", "No, that is not possible because of the 'local write' concept.", "It depends on whether it is a boolean, integer or real-valued algebraic variable." ], correctAnswer: '1' }, { type: 'single-choice', question: "Is bridge_deck.is_open or bridge_light.is_on a good alternative defining expression for algebraic variable car_may_not_drive?", answers: [ "Yes, because when the bridge is not closed, it is open.", "No, because when the bridge is not closed, it may not be fully open." ], correctAnswer: '2' }, { type: 'single-choice', question: "Is not car_may_drive a good alternative defining expression for algebraic variable car_may_not_drive?", answers: [ "Yes, it is completely equivalent.", "No, it is not always equivalent." ], correctAnswer: '1' } ]

Exercise 2

Model the bridge deck and bridge light example without algebraic variables. When you are done, check with the state visualizer whether the behavior is the same as before.

The example model can be modeled without algebraic variables, making use of more locations, as follows:

                                        
                                            automaton bridge_deck:
                                                disc int angle = 0;
                                                event open_further, close_further;

                                                const int step = 10;

                                                location Closed:
                                                    initial;
                                                    edge open_further do angle := angle + step goto InBetween;

                                                location InBetween:
                                                    edge open_further when angle < 90 - step do angle := angle + step;
                                                    edge open_further when angle = 90 - step do angle := angle + step goto Opened;
                                                    edge close_further when angle > 0 + step do angle := angle - step;
                                                    edge close_further when angle = 0 + step do angle := angle - step goto Closed;

                                                location Opened:
                                                    edge close_further do angle := angle - step goto InBetween;
                                            end

                                            automaton bridge_light:
                                                event turn_on, turn_off;

                                                location On:
                                                    initial;
                                                    edge turn_off goto Off;

                                                location Off:
                                                    edge turn_on goto On;
                                            end

                                            automaton car:
                                                event may_drive_now, may_not_drive_anymore;

                                                location MayNotDrive:
                                                    initial;
                                                    edge may_drive_now when bridge_deck.Closed and bridge_light.Off goto MayDrive;

                                                location MayDrive:
                                                    edge may_not_drive_anymore when not bridge_deck.Closed or bridge_light.On goto MayNotDrive;
                                            end
                                        
                                    

This is just one possible answer. Hopefully, you see that the version of the model with algebraic variables is simpler.