Warning: this page is just kept for reference. Do not use oclingo anymore. Instead use clingo.

Overview

oclingo is a system for reactive answer set programming, extending gringo and clasp for handling external modules provided at runtime by a controller.

Installation

Obtaining the oclingo code became difficult because the svn repository hosted sourceforge is broken. But it should be possible to obtain the necessary bits from github.

The oclingo code can be obtained from github.com/grote/oclingo and the required clingo code from github.com/potassco/oclingo.

git clone --branch=clingo-3 git://github.com/potassco/clingo
git clone git://github.com/grote/oclingo

cd oclingo
(cd libprogram_opts; ln -s ../../clingo/libprogram_opts/src)
(cd libprogram_opts; ln -s ../../clingo/libprogram_opts/program_opts)
(cd libclasp; ln -s ../../clingo/libclasp/src)
(cd libclasp; ln -s ../../clingo/libclasp/clasp)
make target=oclingo-app

Be sure to have re2c, the boost library and cmake installed. The binary will be found in build/release/bin/ if everything works out as planned.

Using oclingo

Examples are located in examples/oclingo/ in the source code package. In order to execute an example in this path, run these two commands.

$ oclingo elevator/building.lp elevator/elevator.lp --imax=99 0
$ ./controller.py elevator/online.lp

It is assumed that there is an oclingo binary in some directory from your $PATH environment variable.

oclingo will wait for the controller to connect and then gradually process the inputs by the controller, read from online.lp. Without this file, the controller will prompt the user to provide input. Sending #stop. will terminate the execution of the controller and of oclingo.

Useful Command Line Options

Calling oclingo with the option --help gives you the full list of command line options. The following options might be particularly useful when working with oclingo.

--port=<num>         : Port oclingo daemon should listen to

--import=<arg>         : What head atoms should be imported from external modules
    Default: ext
    Valid:   ext, all
      ext   : Import only heads that have been defined as external
      all   : Import all head atoms

--imin=<num>         : Perform at least <num> incremental solve steps
--imax=<num>         : Perform at most <num> incremental solve steps

--iinit=<num>         : Start to ground from step <num>

The controller also provides some command line options that are available by calling ./controller.py --help.

-n HOST, --host=HOST    Hostname of the online iclingo server. Default:
                        localhost
-p PORT, --port=PORT    Port the online iclingo server is listening to.
                        Default: 25277
-t TIME, --time=TIME    Time delay in seconds between sending input from
                        online.lp to server. Default: 1
-w WAIT, --wait=WAIT    Wait for answer set before sending new input, yes or
                        no. Default: yes
-d, --debug            show debugging output

Providing New Input Faster Than Answer Sets Can Be Found

Sometimes, new information may arrive before a pending problem is solved. If this renders the solution outdated, you might not want to wait until it is found. For this use case, there exists the controller option --wait=no telling the controller not to wait until an answer set is found before sending new information. oclingo then stops its solving process incorporates the new information and restarts the solving.

$ oclingo wumpus/world3.lp wumpus/wumpus.lp --imax=99 0
$ ./controller.py --wait=no -t 0.5 wumpus/online3.lp

The parameter -t 0.5 tells the controller to send new modules from wumpus/online3.lp every half second without waiting.

Writing Encodings for oclingo

oclingo makes extensive use of incremental encodings known from iclingo. Before starting to write oclingo encodings, please make sure you know how to write incremental encodings. Detailed instructions can be found in the potassco guide.

The notion of modules is used to incorporate external knowledge into the evolving program. Atoms that should be imported by the incremental program have to be marked already in the encoding (usually in the #cumulative part). This is done using the #external statement in one of the following ways though it is recommended to always use the slash notation (3rd).

#external atom.
#external predicate(V1, V2).
#external predicate/2.
#external predicate(V1, V2) : vars(V2).

Atoms that are declared as external should not appear in the heads of rules from an incremental encoding (for guaranteeing modularity). Rather, they are supposed to be defined by external modules. It is possible to import atoms from the external module that were not declared external. These atoms will show up in the answer set, but can not be used in the encoding. To enable this behavior, use the --import=all option when starting oclingo.

An external module is supplied by a controller. It always starts with a #step statement that indicates the incremental step at which the module should be imported. If the current processing of the encoding has not arrived at this step, it will first be increased before the external knowledge is added. The external module ends with a #endstep statement. An example follows.

#step 1.
atom :- body.
predicate(a, b).
#endstep.

Note that only ground normal logic programs are accepted by oclingo in the external module.

After the last external input, a #stop statement should be send by the controller.

Using Volatile Queries

One-Time Queries

One-time queries (similar to Prolog) can be used by employing a #volatile statement in the external module. Usually, integrity constraints are used to only show certain answer sets or solve the program regarding a special property. Answer sets fulfilling the property will be displayed (if existing). Afterwards, the query is discarded and the state is the same as before. If the query did not return any answer sets, the incremental step is increased by one as this is the default behavior of iclingo.

#step 3.
#volatile.
:- not property(x).
#endstep.

It is also possible to specify non-volatile rules before the #volatile statement.

An example using volatile queries can be found in examples/oclingo/technical1 using the external modules of online3.lp

Time-Decay

Volatile rules can be made transient for a defined number of steps. After the number of steps is reached, the rule is discarded. Consider the following example.

#step 3.
#volatile : 2.
predicate(a, b).
#endstep.

For step 3, the volatile fact predicate(a, b) is added for 2 steps. In step 3 and 4 all answer sets will contain this fact, while in step 5 it will be gone. Thus, the volatile rules are decaying over time.

A simple example can be found in examples/oclingo/technical1 using the external modules of online4.lp. More sophisticated external modules are in online5.lp.

Using Volatile Rules

In iclingo, you could always use volatile rules that are only valid for the current step and get discarded for the next step. Now, it is also possible to specify the number of steps the rules in the #volatile block should stay valid. This is similar to the time decay feature from above and done using a “: i” notation. Contrary to the queries form above, this new #volatile block is not specified in the external module, but in the encoding itself. It can be used to model a sliding window where rules are automatically discarded after a predefined number of steps. Consider the following example.

#volatile t : 2.
predicate(t).

For the first and second step predicate(1) will be true. But in the third step it will be gone and all rules that have it in their body will be false.

The 2 in the example can also be a term that evaluates to an integer.

#const size = 1.

#volatile t : size + 1.
predicate(t).

Using Assert and Retract Statements

#step 1.
#assert : term(3).
head :- body.
#endstep.

#step 3.
#retract : term(3).
#endstep.

Controlling The Step Counter

When providing external input to oclingo like in the examples above, the default behavior is to increase the step counter as much as needed if there is no answer set for the current step. This also is the default setting for iclingo. It is useful for planning problems which usually only have solutions beyond a certain time step. For other problems this behavior might not be desirable. Volatile external input for example may induce an unsatisfiable problem. Just increasing the step counter until the volatile input is automatically removed and an answer set can be found, is not desired in these cases. Instead, the user of oclingo wants to be informed about the unsatisfiability of the problem, so she can try again.

This can be accomplished by providing a bound in the initial #step statement. An integer preceded by a colon represents the bound.

#step 1 : 3.
:- atom(1).
#endstep.

In this example, the step counter can be increased three times starting at one. So if there have been no answer sets in step four, oclingo will stop there, report unsatisfiability and ask for new input. If there is an answer set already in step one, the bound will have no effect. Its only function is to limit the automatic increment of the step counter. A bound of zero prevents the step counter from being increased completely. A simple and meaningless example can be found examples/oclingo/technical1/online7.lp

Optimization by Forgetting Old Externals

When an incremental encoding involves many external atoms, these atoms accumulate over time in internal data structures of oclingo and consume memory. It is therefore a good idea to let oclingo forget them as soon as it is known that they will not be needed anymore. This can be done using the #forget statement with an integer or a range of integers. External atoms defined at those step will be forgotten and can not be imported anymore.

#step 5.
#forget 1..3.
#forget 4.
atom(5)
#endstep.

After this module has been added, external atoms declared before step 5 should not anymore be defined by external modules provided in the sequel. External atoms that already have been defined will not be forgotten and stay true.

Committing to Certain Answer Sets

For many encodings, there are a multitude of different answer sets. As the process of solving a dynamic problem progresses, one might want to commit to certain answer sets, e.g., because they represent a path already taken in an evolving environment.

Consider the planning problem of a examples/oclingo/little_robot that tries to find its way on a grid to a goal field. The robot.lp uses a choice rule to generate all possible movements and an integrity constraint to eliminate the movements that do not lead to a goal field. If the actual robot carried out a movement in the physical world, answer sets that represent diverging movements do not need to be considered anymore.

% generate moves
1 { go(D, t) : direction(D) } 1 :- not won(t).

% use external predicate to allow commitment to moves
#external robot_moved/2.

% commit to movement of the robot
go(D, t) :- robot_moved(D, t), direction(D).

In order to commit to all those answer sets that correspond to the actual movement, an additional external predicate is introduced. In the example, the predicate robot_moved/2 can be used by the controller to communicate executed movements. These movements were formerly subject to choices over the go/2 predicate and are then fixed.

#step 2.
robot_moved(right,2).
#endstep.

#step 3.
robot_moved(right, 3).
#endstep.

After supplying this online.lp progression, the number of answer sets is reduced because many possible movements are eliminated by committed actions of the robot.

Publications