• Keine Ergebnisse gefunden

3.3 The SALMA Agent Process Definition Language

3.3.2 Agent Control Procedures

A control procedure is the top level element of the SALMA-APDL. As such, it wraps a sequence of statements to define a control flow that can then be assigned to agent processes.

Definition 3.10(Structure of an Agent Control Procedure). An agent control procedure is defined according to the following grammar:

<Procedure> =Procedure(procName: string, body:StatementOrSequence) ; StatementOrSequence=Statement | list<Statement> ;

Statement=Sequence|Act|Assign|If |Switch|While| Select|Iterate|Wait ;

<Sequence> =Sequence( list<Statement> ) ;

Essentially, Definition 3.10 resembles the class hierarchy of Figure 3.8.

However, it can be seen in the definition ofStatementOrSequencethat a Python list can be used for the body. I this case, aSequence instance will be created implicitly. Since Python allows the use of square brackets to buildlist literals, it is possible to write procedures in the following format:

proc=P rocedure([

statement1, . . .

statementn])

This shortcut for statement sequences will be used in several places and is one of the key factors that make the SALMA-APDL usable like a specialized external agent programming language. In the remainder of this section, each statement type that may appear within procedures will be introduced. It starts with the one that is most directly connected to the situation calculus domain model, namely the execution of an action.

Definition 3.11 (Action Execution). Within an agent control procedure, an action execution is triggered by the Act-statement that is defined as follows:

<Act> =Act(actionName: string, arguments:ArgumentList) ; ArgumentList= list<Argument> ;

Argument= string | number | boolean |Variable;

<Variable> =Variable(varName: string [, varType: string]) ;

When anActstatement is reached in the control flow, an action term will be constructed for the given action name and the given arguments, where strings are translated to entity identifiers. When the simulation engine reaches anAct statement, it attempts to execute the given action in the current simulation step. If the created action instance is possible, i.e. the corresponding poss -axiom is satisfied, it will eventually be executed by means of the progression operator. However, if the precondition axiom is not satisfied, the simulation is canceled and an error is risen.

The Python expressions in the arguments of the action are not evaluated when the action term is created but when the statement object is instantiated during the simulation’s setup. Therefore, they will act as constant values with respect to the action execution. Alternatively, instances of the classVariable can be used to mark variables that will be evaluated within the procedure’s context when the statement that contains the variable is executed. For ex-ample, in the multi-robots scenario, the robot’s control procedure contains the statements visible in Figure 3.9 where the action deliver is executed us-ing the previously initialized Variableobjects,targetItemand targetWsas arguments.

Naturally, variables do not only appear in action executions but in many other places. As the grammar above reveals, it is also optionally possible to specified a type in the constructor of the Variable object. However, this is

targetItem =Variable("targetItem") targetWs =Variable("targetWs") . . .

Act("deliver", [SELF, targetItem, targetWs]) . . .

Figure 3.9: Example for an action execution.

only required for variables that are used in the statementsSelectandIterate that are introduced later. The more urgent question for now is how values are actually assigned to variables.

Definition 3.12 (Variable Assignment). An Assign-statement retrieves a value from a value source and assigns it to a variable in the context of the current procedure. Its syntax is defined as follows:

<Assign> =Assign( target:TargetDef, source:ValueSource [, arguments:ArgumentList] ) ;

TargetDef = string |Variable| list< string |Variable> ;

ValueSource= pythonFunction |FluentName|PrologFunctionName| PythonExpression ;

FluentName= string ;PrologFunctionName= string ; PythonExpression= string ;

Here, the target is either a single variable or a list of variables to which the calculated value or values will be assigned (for multiple variables, the value retrieved from the source must be a tuple of appropriate length). Each variable is either identified by its name or by aVariableobject. Thesource, from which the value is retrieved, can be one of the following:

a) A fluent whose name is given insourceand whose instance is specified by the given arguments.

b) A situation-independent Prolog function (i.e. a predicate that returns a value by binding it to a variable in its last argument), whose name is given insourceand to which the given arguments will be passed.

c) A Python function to which a pointer is passed in source and to which the given arguments will be passed. Within the function, it is also possible to access the world’s state, i.e. fluent instances, entity domains, etc. (see Section 3.4.5).

d) A Python expression, given as string insourcethat will be evaluated with Python’s built-in function eval(). Within the expression, there will be an

extensive set of name-value bindings that provide access to variables and fluents (see Section 3.4.5).

In the delivery robots scenario, examples for uses of fluents, Python expres-sions and a Python function can be found within the agent control procedures, for instance in the fragment shown in Figure 3.10 that is reached when a robot has just been assigned to a new task:.

. . .

Assign(targetItem, "task_item", [SELF]), Assign(targetWs, "task_workstation", [SELF]), Assign(tx, "targetItem.xpos"),

Assign(ty, "targetItem.ypos"), . . .

Figure 3.10: Examples for variable assignments within the robot control pro-cedure.

Here, the robot directly accesses the derived fluents task_itemand task_workstation to access the assigned item and the target workstation.

After that, it uses two simple Python expression to access the position of the item. In fact, the SALMA framework provides a dynamic function and attribute mapping system that allows the modeler to access fluents and con-stants in an object-oriented manner, which significantly increases conciseness and readability (see Section 3.4.5). However, sometimes the calculation of the assigned value is too complex to be expressed in a single expression. In these cases, a Python function may be used as a source. For example, Figure 3.11 shows how the control procedure of the coordinator agent uses the function select_item to select the closest item to a given robot that has not been delivered and also is not yet assigned to be delivered, which is checked by item.undelivered. The first argument of this function is set in the Assign -statement with the variablerto which an idle robot has been assigned before.

Eventually, the chosen pair of robot and item is used in anassign_taskaction to allocate the delivery task.

Even this rather simple example shows how valuable the tight integration with Python as a general purpose language is. Although in this situation it would also be possible to perform the calculation within a derived fluent clause in Prolog, it is easy to imagine more sophisticated selection strategies that might involve complex algorithms from fields like Graph theory, optimization, or machine learning. For all of these purposes there exist mature Python libraries that can easily be integrated in functions like the one above. At

defselect_item(rob: Agent, ctx: EvaluationContext=None, **kwargs):

ifrobisNone:

returnNone closest_item = None min_dist = None

foriteminctx.getDomain("item"):

ifitem.undelivered:

dist = np.abs(rob.xpos - item.xpos) + np.abs(rob.ypos - item.ypos) ifmin_distisNoneordist < min_dist:

closest_item = item min_dist = dist returnclosest_item

. . .

Assign(i, select_item, [r]), . . .

Act("assign_task", [SELF, r, i, ws]))])

Figure 3.11: Example use of a Python function as a value source for variable assignment.

the same time, theAssignstatements act as integration points into the agent control procedures that make thedata flow visible immediately.

For making decisions within agent control procedures, the SALMA-APDL defines two of the most common conditional statements, namelyIf andSwitch. Definition 3.13(Conditional Statements). Conditional control blocks can be created with the statementsIfand Switch.

<If> =If( condition:Condition[, arguments:ArgumentList],

then:StatementOrSequence[, else:StatementOrSequence] ) ; Condition= pythonFunction |FluentName|PrologFunctionName|

PythonExpression ;

<Switch> =Switch(CaseOrDefault {,CaseOrDefault } ) ; CaseOrDefault =Case|Default;

<Case> =Case( condition:Condition[, arguments:ArgumentList], then:StatementOrSequence) ;

<Default> =Default(body:StatementOrSequence) ;

As usual, the If-statement tests a condition and executes the contained statement in then (which could also be a sequence) only if the condition is true. Optionally, another statement can be specified in else to be executed when the test fails. For the condition, the same options are available as for the value source of theAssignstatement from Definition 3.12, i.e. a fluent, a Prolog function, a Python function, or a Python expression. In any case, the

given condition source is evaluated in the current context and is expected to return a boolean value.

TheSwitchstatement is inspired by the well-known construct from general purpose programming languages like Java or C/C++. The cases are specified by means of Caseobjects that contain a condition together with a statement.

Optionally, one Default case can be specified that is executed when no con-dition is satisfied. It is worth noticing that these objects are directly given as arguments parameters for Switch instead of as a list, which helps distin-guishing the case collection from a sequence. When the Switch statement is executed, the conditions of every Caseobject are evaluated in the same order they were specified and the first case for which the condition is satisfied is chosen for execution. If no test succeeds and a default case exists, then the statement specified in the Default block is executed. Otherwise, the control flow continues with the next statement after the Switch.

It is clear that conditional statements are indispensable for any agent con-trol language. Often, an Ifis necessary to test a condition before an action is executed. In other cases, the agent decides between multiple alternatives how to proceed. Although the If statement could obviously also be used for this purpose, a Switchconstruct can often be useful for avoiding a deep nesting of statements that would obfuscate the real logic behind the decision.

Examples for the use of both If and Switch can be seen in Figure 3.12.

This figure also contains a loop realized by the While-statement, which is defined next.

Definition 3.14 (While Loops). The SALMA-APDL supports while loops in their usual form:

<While> =While( condition:Condition[, arguments:ArgumentList], body:StatementOrSequence) ;

The condition used for While-loops is built in the same way as that of the If-statement. The statement in the loop body is consequently repeated until the condition fails, and unlike most procedural programming languages, the SALMA-APDL intentionally does not support abreak statement that cancels loop execution. Although having such an option would be convenient in some cases, it could also make the control flow harder to trace and reason about.

Another essential control flow element appears in Figure 3.12 which is required to realize almost any nontrivial agent behavior: the ability to block execution until a condition is fulfilled. This is achieved by theWaitstatement.

Definition 3.15 (Wait Statement). In a SALMA-APLD procedure, a Wait statement can be used to block execution until a condition is true. Its syntax is defined as follows:

PRECONDITION TEST BEFORE DELIVERY:

. . .

If("not self.broken and "

"dist_from_station(self, targetWs) == 0 and carrying(self, targetItem)", Act("deliver", [SELF, targetItem, targetWs]))

MAIN MOVEMENT CONTROL LOOP:

. . .

While("not self.broken and self.next_task != None and "

"(self.xpos != tx or self.ypos != ty)", [ Switch(

Case("self.xpos < tx", Act("move_right", [SELF])), Case("self.xpos > tx", Act("move_left", [SELF])) ),

Wait("self.ready"), Switch(

Case("self.ypos < ty", Act("move_down", [SELF])), Case("self.ypos > ty", Act("move_up", [SELF])) ),

Wait("self.ready")])

Figure 3.12: Example use of the control flow statements in the SALMA-APDL.

<Wait> = Wait( condition:Condition[, arguments:ArgumentList]) ;

When a Wait-statement is executed within an agent process whose con-dition is true, the control flow simply proceeds with the following statement.

However, when the condition is false, the control flow leaves the current pro-cess and continues the execution of other propro-cesses. The propro-cess is blocked and only continued when the condition becomes true. This can only happen due to the change of a fluent value caused by an action performed in another process or an event.

Although the precise simulation semantics will not be discussed until Sec-tion 3.6, it is important to realize that acSec-tion execuSec-tions and blocked Wait statements are the only points in an agent control procedure where context switches occur in the simulation. The parts in between these points are not interleaved with other actions, i.e. they form atomic blocks. This means, for example, that an infinite loop in one process would actually block not only the process that contains it but the whole simulation.

In the robot’s movement control loop shown in Figure 3.12, theWait state-ments are used right after a movement is started to wait until the step has ended, which is indicated by the derived fluentidle. Without waiting for the

idle state, the next move action would be executed before its precondition is fulfilled, which would cause the simulation to be canceled. Although this re-quirement for explicitly awaiting might seem cumbersome at first, this pattern represents the actual behavior of the modeled agent more accurately.

The set of statements defined above would, in principle, be sufficient to define any intended agent behavior. However, the SALMA-APDL offers two additional constructs that are very useful in many situations and actually establish a bridge to logic programming.

Definition 3.16 (Select Statement). The Selectstatement chooses the first combination of argument values that make a Prolog predicate true.

<Select> =Select( predicateName: string, arguments:ArgumentList) ; Different from the Assign statement, the source for Select cannot be defined in Python but has to be either a relational (boolean) fluent, a boolean derived fluent, or a situation-dependent Prolog predicate. When the Select statement is executed, this predicate is called similarly as during the Assign statement. However, every variable that is not bound to a value at that time, and for which one of the entity types is specified as type, is treated as a free variable. For these, the corresponding argument positions are filled with fresh Prolog variables and the type information is used to set up membership constraints with respect the sort domains. Due to Prolog’s evaluation scheme, the system will now use backtracking search to find a combination of values for which the called goal succeeds. If at least one such combination exists, the first one is chosen and each free variable is bound to its corresponding value.

Otherwise, all free variables are set to None.

In many cases, not only the first but all possible solution for a predicate or a fluent should be considered. In this case, a construct is needed that iterates over these solutions.

Definition 3.17 (Iterate Statement). The statement Iterate repeats its nested body for each possible solution of a relational fluent or Prolog pred-icate and binds free variables according to each iteration.

<Iterate> = Iterate( source:ValueSource, arguments:ArgumentList, body: StatementOrSequence) ;

ValueSource= pythonFunction |FluentName|PrologFunctionName| PythonExpression ;

The source for the iteration can be any of those that are also used for vari-able assignment. However, their meaning is different: for fluents and Prolog predicates, the iteration is performed over all possible solutions in the sense

r, i, ws = makevars(("r", "robot"), ("i", "item"), ("ws", "workstation")) Procedure([

Iterate("self.request_queue", [ws], [ Select("unassigned", [r]),

Assign(i, select_item, [r]), If("i != None and r != None",

Act("assign_task", [SELF, r, i, ws]))])])

Figure 3.13: Example for the use of Selectand Iterate.

of Prolog’sfindall operator (see [DEDC12, chap. 5.3]). In contrast to that, a Python function or expression is expected to return a list. This list can either contain one individual value for each cell, if only one free variable was specified, or tuples with one values for each free variable.

For both the Selectand the Iterate statement, examples can be found within the control procedure of the coordinator agent, which is shown in Fig-ure 3.13. Here, the iteration is performed over the list of requests that the coordinator has received until the loop is entered. In each iteration, the vari-ablewsis set to the next workstation from the request queue. Within the loop’s body, aSelectis used to retrieve the first robot that isunassignedaccording to the corresponding derived fluent. As described above, this robot is stored in the variable r and passed as an argument to the function select_item that was shown in Figure 3.11 whose return value is assigned to the variable i. Finally, if a value could be found for both r and i, the task is assigned using theassign_taskaction. The variablesr,iandwsare setup beforehand using the utility function makevarsthat simply returns one Variableobject for each tuple in the argument list, where the tuples contain both the name of the variable and its type. In fact, in this case the type information is only necessary forr, which is used in theSelectstatement. However, since it adds valuable information to the model, it is certainly a good practice to add type information in all variable declarations.