Advanced Knowledge: Plan and Rule Annotations

Analogously to Java, AgentSpeak(L++) also supports the concept of annotations.

In AgentSpeak(L++) → plans and logic rules can be modified in their execution behaviour by adding annotation(s) to them.

Contents [Hide]

Annotations to Plans and Rules

Plans: Our plan grammar specifies that (optional) annotations, prefixed with an @ symbol, can be placed in front of plan trigger, i.e.

annotationsplan_triggerliteralplandefinitionDOT

@annotation
+!plan <-
    …
.

Rules: Analogously LightJason(L++) allows adding annotations to rules.

annotationsliterallogicalruledefinitionDOT

@annotation
rule
    :- conditions
    :- conditions
    …
.

Note: Multiple annotations can be combined, e.g. for plans

@annotation1
@annotation2
+!plan <-
    …
.

Built-in Annotations

We currently support two built-in annotations, i.e. @parallel and @atomic, which are explained in the following in further detail.

ATATOMICPARALLEL

@atomic

The @atomic annotation defines a plan to always succeed by default. This behaviour is useful in cases where failure of → actions can be expected but do not constitute erroneous behaviour or results.

Usage:

@atomic
+!plan <-
    …
.

The plan will succeed regardless of failing actions or sub-plans.

For a plan

@atomic
+!plan <-
    action1();
    !!immediate_subplan;
    !postponed_subplan;
    action2();
.
the execution flow is as depicted below

Knowledgebase - AnnotationsLayer 1action1()!!immediate_subplan!postponed_subplanaction2()plan succeededfailExecuted in next cycle.No information regardingsuccess or failure in this cycle.failfailsuccsuccsucc@atomic@atomic

@parallel

The @parallel annotation defines a plan to execute every action and → goal in its body in parallel, via fork-join mechanism. This behaviour is useful to speed up execution of independent actions and sub-plans.

Notes:

  • If any of the parallel executed body elements fails, the whole plan fails.
  • The annotation only applies to plans triggered by !! and executed in the current cycle. Plans triggered via ! will be independently executed in the following cycle. For more information on triggering see → Plan Triggering Techniques.

Usage:

@parallel
+!plan <-
    …
.

For a plan

@parallel
+!plan <-
    action1();
    !!immediate_subplan;
    !postponed_subplan;
    action2();
.
the execution flow is as depicted below

Knowledgebase - AnnotationsLayer 1!postponed_subplan!!immediate_subplanaction1()action2()plan succeededplan failedExecuted in next cycle.No information regarding successor failure in this cycle.forkjoin@parallel

Examples

The agent script

!main.

+!main <-
    !!plan << generic/print(“plan failed!”)
.

+!plan <-
    generic/print(“foo”);
    !!subplan1;
    !!subplan2;
    generic/print(“bar”)
.

+!subplan1 <-
    generic/print(“sub-plan1”);
    fail
.

+!subplan2 <-
    generic/print(“sub-plan2”)
.

will only yield

foo
sub-plan1
plan failed!

as +!subplan1 fails and the execution stops. The << represents a → repair action.

Adding @parallel to +!plan will execute every goal trigger and action in parallel, yielding

bar
sub-plan1
foo
sub-plan2
plan failed!

(ordering of output might vary). It can be observed that, despite executing every element in the body of +!plan, the whole plan still fails, as +!subplan1 fails.

Further adding @atomic to +!plan will result in successful execution of +!plan, returning

foo
bar
sub-plan1
sub-plan2

(ordering might vary). The same effect could also be achieved by preventing !subplan1 to fail via @atomic, which is left as an exercise to the reader.

The complete example with @atomic and @parallel would be

!main.

+!main <-
    !!plan << generic/print(“plan failed!”)
.

@atomic
@parallel
+!plan <-
    generic/print(“foo”);
    !!subplan1;
    !!subplan2;
    generic/print(“bar”)
.

+!subplan1 <-
    generic/print(“sub-plan1”);
    fail
.

+!subplan2 <-
    generic/print(“sub-plan2”)
.