Warning: this page is just kept for reference. Do not use oclingo anymore. Instead use clingo.
oclingo is a system for reactive answer set programming, extending gringo and clasp for handling external modules provided at runtime by a controller.
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.
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.
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
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.
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.
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
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
.
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).
#step 1.
#assert : term(3).
head :- body.
#endstep.
#step 3.
#retract : term(3).
#endstep.
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
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.
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.