In this section, we articulate the criteria for ensemble formation employed for the use case in Sect. 3 via the TCOEL concepts. We also briefly describe a prototype implementation of TCOEL containing the engine forming the ensembles at runtime and trait library—TCOEF framework (middleware) available at http://github.com/d3scomp/tcoef.
We split the criteria for ensembles’ formation (used in specification of ensemble types, see definitions in Sect. 2B) in two categories: (i) based on the core concepts that are independent of a particular SSA domain and (ii) based on the concepts that depend on particular SSA domains; we group the latter into reusable traits. By trait, we denote a set of concepts that extend the core of TCOEL and can be used optionally; this is similar to object-oriented languages where a trait (sometimes called mixin) is typically a set of orthogonal methods that can be attached to a class to extend its behavior.
The concepts featured by a trait are specific to a particular SSA domain (e.g., connected mobility, emergency coordination, home automation, robotic swarm) or to a particular aspect of the domain (e.g., navigation in 2D space). This makes the traits reusable across multiple use cases. For example, the “map” trait reflects the concept of spatial proximity and can be reused in applications that belong either to the connected mobility domain or to the domain of emergency coordination in a city. In contrast, criteria that do not depend on a particular SSA domain are universal in ensemble type specification, i.e., they typically make sense in any application domain. For example, the “type” of an agent is a possible criterion for ensemble formation in any application (irrespective of its domain).
Thus, an ensemble type in an SSA can be specified by using the domain-independent concepts and augmenting them with selected traits.
TCOEL, an example
In TCOEL, we represent (specify) both component and ensemble types as Scala classes that extend the abstract classes Component and Ensemble, respectively. Furthermore, we use the power of Scala to define new control structures (constructs for short) to express the operation steps of components and to declare membership condition and task computation of an ensemble. Technically, these constructs are realized as Scala functions with a “by-name” parameter [9].
We exemplify our approach on a part of the RCRS use case (Sect. 3) illustrated in Fig. 1. Here, the abstract classes Component and Ensemble are inherited by application-specific classes—lines 4, 41, 66, 81, 113. In particular, a FireStation component is expected to form an ensemble with FireBrigade components in order to extinguish the fire of a building and protect the surrounding ones. Similarly, there are components representing other RCRS agents (AmbulanceTeam, etc.). Due to space limits, Fig. 1 does not show how components are actually instantiated; for simplicity, let us assume there exist a singleton of FireStation and n instances of FireBrigade. To achieve the required ensembles, the ensemble FireCoordination (line 66), initiated by FireStation, introduces two sets of child ensembles—extinguishTeams and protectionTeams (lines 68–73). FireCoordination is a root ensemble (Sect. 2B). The members of these child ensembles cover disjoint locations on the city map. Members of an ensemble ProtectionTeam are selected from brigades (line 81, instances of FireBrigade) and are associated with a particular fireLocation; the selection is determined by the membership construct (lines 86–99), specifying that only the instances of FireBrigade which are either idle or already at the scene given by fireLocation are considered. Additionally, they have to be at a distance from which they can reach the fireLocation before the building there burns down. (Otherwise, there is no reason to go there.) Plus, their number has to be 2 or 3. In the taskComputation construct (lines 106–110), the selected brigades are assigned to specific fire locations. The ensemble type ExtinguishTeam is specified similarly.
Below we elaborate more on the TCOEL component and ensemble semantics.
Components
In support of self-adaptation, a component operates on a periodic basis by performing the classical MAPE-K loop [19]. This is done in the following four steps activated by TCOEF.
In Monitoring, the component senses data from its sensors and typically receives messages from the other components that are initiators of the ensembles it is a member of; as a follow-up, it accordingly updates its knowledge (e.g., line 5), both local and mirror (the types of which are defined in model). In TCOEL, this step is specified by the sensing construct (lines 8–13, 44–46).
In Analysis, the component first determines the potential activities it can perform, given its state of knowledge. To achieve its conceptual autonomy and play its role in the current requirement of ensemble, the component’s activities are controlled by its behavior states (BState, line 6). Each state determines a particular component activity (e.g., going to refill water, seeking refuge in case a firefighter is hurt). A component can be in multiple behavior states at the same time, which corresponds to the ability to simultaneously fulfill several roles. The construct constraints (lines 15–19) serves to indicate (i) dependency of a behavior state on a particular value in component knowledge and (ii) conflicting states (e.g., moving and observing environment). To break ties in situation where different conflicting behavior states could be selected given the current valuation of knowledge, the utility construct provides a utility function, allotting weight to behavior states (lines 25–27).
In Planning initiation of ensembles takes place. This involves solving the constraint optimization problem (Sect. 2C) stemming from the ensemble type specification (membership condition, utility function) and from the current knowledge and utility value of the potential component members. This initiation is captured by the ensembleResolution construct (lines 48–52) employed in the component that becomes the initiator of the ensembles resulting from the resolution.
In Execution (the actuation construct—lines 20–23 and 54–63), the component performs the actuation and typically sends knowledge updates to the initiators of the ensembles it is a member of. If the component is an initiator of an ensemble (contains the construct ensembleResolution), then the final step in actuation is a signal for dismantling “its” ensemble(s)—this is completed once all their tasks are finished (specified in the taskComputation constructs).
As an aside, the periodic operation of components is not visible in Fig. 1, since component instances are activated by the RCRS simulator in its simulation steps.
Ensembles
The specification of an ensemble type is structured following the core concepts of ensemble formation as discussed in Sect. 2A. The selection of components, based on their type, is represented by the role construct (line 82). It determines the potential components that can take responsibility in the ensemble in the given role. The actual selection of components is then based on the membership condition (membership construct—lines 75–78 and 86–99) and the utility function (soft optimization rule defined by the utility construct—lines 101–104). For example, in the ProtectionTeam ensemble type, membership mandates that utility is computed as inversely proportional to the travel time needed for each selected member brigade to get to fireLocation. Strategic-level task computation takes the form of updating coordination-relevant knowledge of the ensemble’s members (taskComputation construct—lines 106–110). The membership condition includes both the cardinality constraints on the number of components (agents) and the domain-dependent constraints pertaining to geographical proximity which exploit the concepts featured by the inherited traits (Sect. 4B).
As to writing membership conditions in nested ensembles, there is a simple convention: Assuming the parent ensemble type P defines its membership condition MP, and a direct child ensemble type C defines its membership condition MC, then the actual membership condition of C is MP and MC. This reflects the rule that a member component of an instance of C has to be also a member component of an instance of P.
Expressivity through domain-dependent traits
In a membership condition, it is not easy to express real-world constraints such as that one component is spatially close to another one, or that a building does not burn down before a firefighter unit reaches the building, etc. The articulation of such conditions strongly depends on the particular SSA domain. To build support for all the possible types of conditions to the core of the specification language is not only impractical, since the language would be quite complex and hard to learn, but even impossible, as all the possible SSA domains cannot be foreseen. Plus, a single application typically would not need all the condition types. Indeed, all the examples in Sect. 3 specify conditions over spatial distances and estimated travel times. However, while the RCRS use case prescribes conditions over estimates of fire spreading/burning speed, a connected mobility system would, e.g., prescribe conditions over estimates of traffic congestions and vehicle speeds, etc. Therefore, in our approach, all these domain-dependent condition types are to be designed as reusable traits to be picked up and used in compliance with the needs of a specific application whenever possible.
In the rest of the section, we overview three traits that are already available in TCOEF. As with the core concepts, we illustrate two of them on the RCRS example in Fig. 1. On lines 1–2, there are the map trait (Map2DTrait) and data prediction trait (StateSpaceTrait); furthermore, there is also a specific RCRSConnectorTrait connecting the language runtime (TCOEF) with the Rescue simulator. (It creates particular agents and processes messages from/to the simulator—not explained here.) Technically, TCOEL traits are developed as Scala traits.
Map trait
This trait serves to capture spatial–temporal relations between the components to be included in an ensemble. The typical use is, for example, to select the entities that are close to each other or close to a particular location in terms of travel time. An example is on lines 83, 93, 102–103. Line 82 computes the shortest routes to fireLocation (via Dijkstra’s algorithm). Lines 93 and 102–103 query the computed travelTime needed for a FireBrigade to reach fireLocation.
Data prediction trait
This trait serves to form ensembles based on the prediction of a data value in SSA. Such predictions can rely either on state-space models that characterize data evolution based on physical processes [20] or on machine learning models that capture patterns and trends in historical data. Examples of application of this trait include (i) the team of “agents within travel time less than the estimated time until building B is burnt out” and (ii) the team of “agents within travel time less than the estimated time-to-survive of victim V.”
In TCOEL, the former is captured by lines 84–85 and 96. Lines 84–85 initialize a predictor of how quickly a particular building (at fireLocation) burns out based on its burning model represented as an ordinary differential equation (ODE); such a model is assumed to be associated with each building. The initial conditions for ODE are the current time and current burnoutStage of the building.
The predictor uses a solver (i.e., a numerical integrator) to solve ODE for a specified point of time (line 96). By combining Map2DTrait and StateSpaceTrait, lines 93–96, it is ensured that “All agents selected for the team have to be able to reach the building (i.e., travelTime is not None) while the burnoutStage of the building, given the travelTime the agent needs to reach it, has to be below 0.9 (the building is not burnt out yet).”
Statistics trait
This trait offers the possibility to construct an ensemble based on statistical evidence about the behavior of certain stochastic processes in the system. Here, we build on our previous work in mode switching based on statistical tests [21]. Due to space constraints, we do not demonstrate this on the example in Fig. 1 since this would necessitate including other parts of the scenario; instead, we give an illustration in the following.
Consider an agent team that heavily relies on radio communication, so that it can be formed only if “The expected probability of packet delivery over the radio is 90 percent or more, evaluated over the last hour with a confidence of 95%.” (Technically, such parameters can be set in the RCRS simulator.) This would be captured in TCOEL as msgDelivery (time - 3600, time).probability > 0.9 withConfidence 0.95, where msgDelivery is a Boolean time series recording whether an expected packet was received or not. The whole expression denotes a one-sided statistical test whether one can reject the null hypothesis that the samples over the last hour have the probability of being true less or equal to 0.9 with significance level α = 0.05.
TCOEF
TCOEF is a runtime framework (middleware in the form of a Scala library) which executes the individual steps of component’s MAPE-K loop and takes care of resolving ensembles. Furthermore, it provides a basic extensible library of reusable traits (Sect. 4B).
Internally, TCOEF translates the component instances and ensemble types to a constraint optimization problem (COP) that describes optimal instantiation of ensembles as defined in Definition 4. The COP which encodes instantiation of ensembles based on their respective ensemble instance templates \( \left( {E_{i} , V_{P}^{i} } \right) \) as generated by functions gsdom (Definition 1). This instantiation starts from root ensembles instances (Definition 3 and line 42 in Fig. 1) and goes recursively over the sub-ensemble groups (i.e., G in Definition 1). The membership of a component in an ensemble instance and sub-ensemble instances is encoded as Boolean variables. Membership conditions are encoded as hard constraints and utility functions as soft constraints. The rules that determine whether an ensemble is valid (Definition 2) and determine ensemble hierarchies are also encoded as hard constraints.
A solution found by the constraint solver corresponds to valid instantiation of root ensemble instances. The optimal solution found by the solver then implies the optimal instantiation of ensemble hierarchy (or hierarchies).