Here is the list of the high-level IndiGolog constructs that are
used in writing procedures. A and B denote programs. They can be
single primitive actions or sequences of primitive actions/procedure
calls. In IndiGolog sequences need to be included in square brackets.
X denotes either a single variable or a list of variables (in square
brackets).
no_op - empty action
A,B sequence of actions
?(<condition>) - test whether the condition
is true
if(<condition>,A,B) if <condition>
is true execute A, otherwise execute B.
Note: no_op can replace either A or B.
while(<condition>,A) while loop. While
<condition> is true execute A.
proc(<name of procedure>,<body of procedure>).
procedure definition. Procedures can be parametrized, so
we can write something like proc(foo(a,b),[action1,action2]).
<name of procedure>(<parameters>) or
<name of procedure> procedure call.
ndet(A,B) nondeterministic choice. Choose
nondeterministically between program A and program B.
pi(X,A) nondeterministic choice of arguments.
Nondeterministically choose a binding for the variable(s) in X
and execute the program A for this binding.
star(A) nondeterministic iteration. Perform
A zero or more times.
conc(A,B) concurrent execution. Execute
programs A and B concurrently. Concurrent processes are modeled
as interleavings of the primitive actions involved.
pconc(A,B) prioritized concurrency. Program
A has higher priority than program B.
Concurrency Note: A process may become blocked when it
reaches a primitive action whose preconditions are false or test
action ?(<cond>) whose condition <cond> is false.
Then, execution of the program may continue provided another process
executes next. In case of prioritized concurrency the lover-priority
process can only be executed when the higher-priority one is done
or blocked.
iconc(A) concurrent iteration. It is like
nondeterministic iteration star(A) , but the instances
of A are executed concurrently rather than sequentially.
interrupt(<condition>,<body>)
Interrupt. If the interrupt get control from higher priority process
and the <condition> is true, the interrupt is triggered
and its body is executed.
interrupt(X,<condition>,<body>)
Interrupt. If the interrupt get control from higher priority process
and the <condition> is true for some binding of the variables,
the interrupt triggers and the body is executed with the variables
taking these values.
Once the body completes the execution, the interrupt may trigger
again.
prioritized_interrupts(<list of interrupts>)
Prioritized Interrupts. The interrupts are listed by their
priority - the first interrupt has the highest one and so on.
This construct is used quite commonly when writing controllers
and reactive programs. You can think of this operator as an event
loop: once prioritized_interrups gets control, it
keeps executing. To start with, the fist interrupt
clause is checked and executed if its condition evaluates to truth.
Then the next interrupt is checked and possibly executed,
and so on. The highest priority interrupts are the ones that require
immediate response. The lower level interrupts can be checked
or executed only when the higher priority ones do not fire. Of
course, the condition can be set to 'true ' so that
the interrupt fires all the time.
search(A) By default IndiGolog programs
are executed in on-line fashion: all the nondeterministic
choices are treated as random ones and, and any action selected
is executed immediately. On the other hand, a program in the search
block is executed off-line. The interpreter must find a
sequence of actions constituting a legal execution of the program
A, before actually executing them.
To illustrate the difference between on-line and off-line execution
we give this example:
ndet(a,b), A, ?(c)
Here a and b are primitive actions, A is
a program, c is some condition. The default way for IndiGolog
interpreter to execute this program is to choose randomly either
a or b, then execute A and then test whether
c holds. It may happen that executing a and then
A makes c false, thus making the program to fail.
On the other hand if we put the above program inside a search
block, the interpreter will search for a successful execution
of the program first, and then execute it. This way we are guaranteed
to find a successful execution if there is one.
After the sequence of actions was found in the search block, it
needs to be checked constantly to see if it is still valid. It
is important since IndiGolog supports exogenous actions. If the
previously found sequence of actions is no longer valid, replanning
is done.
Conditions

IndiGolog supports
quite complicated boolean conditions. Binary boolean operators
such as and and or can be used, as well as unary negation
operator neg. All the operators have to used in prefix notation.
In addition to the above operators IndiGolog also supports some
construct, which is equivalent to existential quantification:
some(X,<boolean expression>) X is a single
variable or a list of variables. This is true if there exists a
binding for the variables in X, that makes the boolean expression
true.
Notes

Variables in IndiGolog are introduced in constructs
such as pi, some and a version of interrupt.
Unlike Prolog variables, they start with lower-case letters.
Currently domain theories must be expressed in Prolog, so the interpreter
depends on dynamic closed-world assumption where it is assumed
that whenever a test is required, the on-line interpreter at
that point has complete knowledge of the fluents in question
to evaluate the test without having to reason by cases, etc.
A very important feature of IndiGolog is its support for sensing
actions and exogenous events.
Sensing Actions

Sensing actions are designed to get/update the value of certain fluent.
The value can be calculated or received from other agent or sensor.
Sensing actions are defined in the following way:
prim_action(<sensing action name>). The
usual action declaration.
poss(<sensing action name>,<condition>).
The usual precondition axiom.
execute(<sensing action name>, X) :- <sensing action
body>. Here X is a variable from the body of the
action that is bound to the sensed result upon execution of the body.
senses(<sensing action name>,<name of the fluent this
action updates>). This statement links the sensing
action with the fluent that it updates.
Note: Sensing actions cannot be used inside a search block.
Exogenous
Actions

Agents coexist with other agents in a dynamic world. Other agents
perform action that can change the environment, therefore agents
must try to detect what exogenous actions have occurred in the world.
IndiGolog provides a way to define monitoring routines for detecting
exogenous events. The monitoring routines (provided their precondition
axioms are satisfied) are tried before each primitive action. An
exogenous action is considered taken place when its monitoring routine
succeeds. In this case the successor state axioms for this particular
exogenous action will tell the interpreter which fluents are to
change their values.
exog_action(<name of exo action>). Declare
exogenous action.
exog_occurs(<name of exo action>) :- <body of exo action>.
Define the monitoring routine. The body is written in Prolog.
poss(<name of exo action>,<condition>).
Precondition axiom. Normally the condition will be 'true',
but in some cases it may be useful to suspend or cancel the execution
of the exogenous event detection routines and the condition will
be nontrivial.
causes_val(<name of exo action>,<fluent name>,<new
value>, <condition>). This specifies how
the fluent <fluent name> changes its value after certain exogenous
action occured.
|