A framework for the integration (embedding) of Logic Programming in external systems for generic applications.
It helps developers at designing and implementing complex reasoning tasks by means of solvers on
different platforms.
The framework can be implemented in a object-oriented programming language of choice, easing and guiding the generation of suitable libraries for the use of specific solvers on selected platforms. We currently provide 3 implementations (in Java , in Python and in C# ) and ready-made libraries for the embedding of the ASP (Answer Set Programming) solvers DLV, clingo and DLV2 and the PDDL (Planning Domain Definition Language) cloud solver Solver.Planning.Domains (SPD) on the Desktop platform and of DLV and SPD on the Mobile (Android™) one (available only for the Java version).
However, the framework has been designed to be easily extensible and adaptable to different solvers and platforms. It is worth to notice that solvers are invoked in different modes; for instance, SPD is invoked via a remote connection, while for the other, binaries are effectively embedded and natively executed.
The general architecture of EmbASP is depicted in the following figure: it is intended as an abstract framework to be implemented in some object-oriented programming language.
In addition, this architecture can be specialized for different platforms, logic languages and solvers.
The figure also reports general dependencies among the main modules.
The framework architecture has been designed by means of four modules:
Core
,
Platforms
,
Languages
, and
Systems
. In the following, we denote with "solver" a logic-based system that is meant to be
used by external application with the help of EmbASP.
The
Core
module defines the basic components of the Framework.
The
Handler
component mediates the communication between the Framework and the user that can provide
it with the input program(s) via the component
InputProgram
, along with any desired solver's option(s) via the component
OptionDescriptor
. A
Service
component is meant for managing the chosen solver executions.
Two different execution modes can be made available: synchronous or asynchronous. While in the synchronous mode any call
to the execution of the solver is
blocking (i.e., the caller waits until the reasoning task is completed), in asynchronous
mode the call is non-blocking: a
Callback
component notifies the caller once the reasoning task is completed. The result of the
execution (i.e., the output of the logic system) is handled by the
Output
component, in both modes.
The
Platforms
module is meant for containing what is platform-dependent; in particular, the
Handler
and
Service
components from the
Core
module that should be adapted according to the platform at hand, since they take care
of practically launching solvers.
The
Languages
module defines specific facilities for each supported logic language.
The generic
Mapper
component is conceived as an utility for managing input and output via objects, if the
programming language at hand permits it.
The sub-module ASP comprises components such as
ASPInputProgram
that adapts
InputProgram
to the ASP case, while
AnswerSet
and
AnswerSets
represent the
Output
for ASP. Moreover the
ASPMapper
allow the management of ASP input facts and answer sets via objects.
Similarly, the sub-module PDDL includes
PDDLInputProgram
,
Action
,
Plan
and
PDDLMapper
.
The
Systems
module defines what is system-dependent; in particular, the
InputProgram
,
Output
and
OptionDescriptor
components from the
Core
module should be adapted in order to effectively interact with the solver at hand.
As mentioned above, we provide a Java implementation of the EmbASP framework.
The following figure provides some details about classes and interfaces of the implementation.
Each component in the
Core
module has been implemented by means of an homonymous class or interface.
In particular, the
Handler
class collects
InputProgram
and
OptionDescriptor
objects communicated by the user.
For what the asynchronous mode is concerned, the class
Service
depends from the interface
Callback
, since once the reasoning service has terminated, the result of the
computation is returned back via a class
Callback
.
In order to support a new platform, the
Handler
and
Service
components must be adapted.
As for the Android platform, we developed an
AndroidHandler
that handles the execution of an
AndroidService
, which provides facilities to manage the execution of a solver on the
Android platform.
Similarly, for the desktop platform we developed a
DesktopHandler
and a
DesktopService
, which generalizes the usage of a solver on the desktop platform, allowing
both synchronous and asynchronous execution modes.
This module includes specific classes for the management of input and output to ASP and PDDL solvers.
The
Mapper
component of the
Languages
module is implemented via a
Mapper
class, that allows to translate input and output into Java objects.
Such translations are guided by
Java Annotations, a form of metadata that mark Java code and provide
information that is not part of the program itself: they have no direct
effect on the operation of the code they annotate.
In our setting, we make use of such feature so that it is possible to translate facts into strings and vice-versa via two custom annotations, defined according to the following syntax:
@Id (string_name)
: the target must be a class, and defines the predicate name (in
the ASP case) and the action name (in the PDDL case) the class is
mapped to;
@Param (integer_position)
: the target must be a field of a class annotated via
@Id
, and defines the term (and its position) in the atom (in the ASP
case) and in the action (in the PDDL case) the field is mapped to.
By means of the Java Reflection mechanisms, annotations are examined at runtime, and taken into account to properly define the translation.
If the classes intended for the translation are not annotated or not correctly annotated, an exception is raised.
In addition to the
Mapper
, this module features two sub-modules which are more strictly related
to ASP and PDDL.
The classes
DLVAnswerSets
,
ClingoAnswerSets
,
DLV2AnswerSets
and
SPDPlan
implement specific extensions of the
AnswerSets
or
Plan
classes, in charge of manipulating the output of the respective solvers.
Moreover, this module can contain classes extending
OptionDescriptor
to implement specific options of the solver at hand.
As mentioned above, we provide a Python implementation of the EmbASP framework.
The following figure provides some details about classes and interfaces of the implementation.
Each component in the
Core
module has been implemented by means of an abstract class or generic
classes that will specialize in the following packages.
In particular, the
Handler
class collects
InputProgram
and
OptionDescriptor
objects communicated by the user.
For what the asynchronous mode is concerned, the class
Service
depends from the interface
Callback
, since once the reasoning service has terminated, the result of the
computation is returned back via a class
Callback
.
In order to support a new platform, the
Handler
and
Service
components must be adapted.
For the desktop platform we developed a
DesktopHandler
and a
DesktopService
, which generalizes the usage of a solver on the desktop platform, allowing
both synchronous and asynchronous execution modes.
This module includes specific classes for the management of input and output to ASP and PDDL solvers.
The
Mapper
component of the
Languages
module is implemented via a
Mapper
class, that allows to translate input and output into Python objects.
Such translations are guided by
Predicate
abstract class, also present in the module.
To make possible translate facts into strings and vice versa, the classes that want to represent a predicate, must extend
the abstract class
Predicate
, and must be implemented by including the following code:
predicateName="string_name"
: must be entered as a class field and must contain the predicate
name (in the ASP case) or the action name (in the PDDL case) to map;
[("class_field_name_1", int), ("class_field_name_2"), ...]
: Is a list that must be passed to super in the constructor, and
must contain so many tuples how many are the class field, containing
the field name, sorted by the position of the terms they represent,
and optionally the keyword
int
if the field represents an integer.
Thanks to the structure of the
Predicate
class, this information is passed to the
Mapper
class, to correctly perform the translation mechanism.
If the classes intended for the translation are not constructed correctly in this way, an exception is raised.
In addition to the
Mapper
, this module features two sub-modules which are more strictly related
to ASP and PDDL.
The classes
DLVAnswerSets
,
ClingoAnswerSets
,
DLV2AnswerSets
and
SPDPlan
implement specific extensions of the
AnswerSets
or
Plan
classes, in charge of manipulating the output of the respective solvers.
Moreover, this module can contain classes extending
OptionDescriptor
to implement specific options of the solver at hand.
In order to use the framework in your applications you have to import it as module on Android Studio.
In the following, we describe an the actual usage of the framework by means of a running example; as a use case, we will develop a simple Android application for solving Sudoku puzzles.
The complete code of this example is freely available here. Please note that the zip file contains an Android Studio (version 2.3) project and uses the version 3.1.0 of EmbASP.
The framework features a annotation-guided mapping, offered by the ASPMapper component, for two-way translations between strings recognizable by ASP solvers and objects in the programming language at hand, directly employable within applications. By means of this feature, the ASP-based aspects can be separated from the Java coding: the programmer doesn't even necessarily need to be aware of ASP.
Let us think of a user that designed (or has been given) a proper logic program P to solve a sudoku puzzle and has also an initial schema. We assume that the initial schema is well-formed i.e. the complete schema solution exists and is unique. A possible program P is embedded in the complete example, that, coupled with a set of facts F representing the given initial schema, allows to obtain the only admissible solution.
By means of the annotation-guided mapping, the initial schema can be expressed in forms of Java objects. To this extent, we define the class Cell, aimed at representing the single cell of the sudoku schema, as follows:
@Id("cell")
public class Cell {
@Param(0)
private int row;
@Param(1)
private int column;
@Param(2)
private int value;
[...]
}
It is worth noticing how the class has been annotated by two custom annotations, defined according to the following syntax:
Thanks to these annotations the ASPMapper class will be able to map Cell objects into strings properly recognizable from the ASP solver as logic facts of the form cell(Row,Column,Value). At this point, we can create an Android Activity Component, and start deploying our sudoku application:
public class MainActivity extends AppCompatActivity {
[...]
private Handler handler;
@Override
protected void onCreate(Bundle bundle) {
handler = new AndroidHandler(getApplicationContext(), DLVAndroidService.class);
[...]
}
public void onClick(final View view){
startReasoning();
[...]
}
public void startReasoning() {
InputProgram inputProgram = new InputProgram();
for (int i = 0; i < 9; i++){
for (int j = 0; j < 9; j++)
try {
if(sudokuMatrix[i][j]!=0) {
inputProgram.addObjectInput(new Cell(i, j, sudokuMatrix[i][j]));
}
} catch (Exception e) {
// Handle Exception
}
}
handler.addProgram(inputProgram);
String sudokuEncoding = getEncodingFromResources();
handler.addProgram(new InputProgram(sudokuEncoding));
Callback callback = new MyCallback();
handler.startAsync(callback);
}
}
The class contains an Handler instance as field, that is initialized when the Activity is created as an AndroidHandler. Required parameters include the Android Context (an Android utility, needed to start an Android Service Component) and the type of AndroidService to use, in our case a DLVAndroidService.
In addiction, in order to represent an initial sudoku schema, the class features a matrix of integers as another field where position (i,j) contains the value of cell (i,j) in the initial schema; cells initially empty are represented by positions containing zero.
The method startReasoning is in charge of actually managing the reasoning: in our case, it is invoked in response to a click event that is generated when the user asks for the solution. It is firstly created an InputProgram object that is filled with Cell objects representing the initial schema, which is then provided to the handler; then it is provided with the sudoku encoding. It could be loaded, for instance, by means of an utility function that retrieves it from the Android Resources folder, which, within Android applications, is typically meant for containing images, sounds, files and resources in general.
At this point, the reasoning process can start; since for Android we provide only the asynchronous execution mode, a callback object is in charge of fetching the output when the ASP system has done.
Finally, once the computation is over, from within the callback function the output can be retrieved directly in form of Java objects. For instance, in our case an inner class MyCallback implements the interface Callback:
private class MyCallback implements Callback {
@Override
public void callback(Output o) {
if(!(o instanceof AnswerSets))
return;
AnswerSets answerSets=(AnswerSets)o;
if(answerSets.getAnswersets().isEmpty())
return;
AnswerSet as = answerSets.getAnswersets().get(0);
try {
for(Object obj:as.getAtoms()) {
Cell cell = (Cell) obj;
sudokuMatrix[cell.getRow()][cell.getColumn()] = cell.getValue();
}
} catch (Exception e) {
// Handle Exception
}
displaySolution();
}
}
The framework is released as JAR file to be used on a Desktop platform, therefore it can be easily imported and used in any Java project.
The SPD solver is invoked via an HTTP connection using JSON files as data interchange format. In order to manage them, the framework needs the json-simple library (you can download the JAR and include directly in your project or you can use Gradle or Maven)
The framework is released as EGG file to be used on a Desktop platform, it can be easily installed in a Python installation.
The framework is stand alone and does not need any library for its operation, except for a Python installation.
In the following, we describe an the actual usage of the framework by means of a running example; as a use case, we will develop a simple Desktop application to solve the blocks-world problem.
The complete code of this example is freely available here. Please note that the zip file contains an Eclipse (Neon) project and uses the version 3.1.0 of EmbASP.
We will make use of the annotation-guided mapping, in order to retrieve the actions constituting a PDDL plan via Java objects. To this purpose, the following classes are intended to represent possible actions that a blocks-world solution plan can feature:
@Id("pick-up")
public class PickUp {
@Param(0)
private String block;
[...]
}
@Id("put-down")
public class PutDown {
@Param(0)
private String block;
[...]
}
@Id("stack")
public class Stack {
@Param(0)
private String block1;
@Param(1)
private String block2;
[...]
}
@Id("unstack")
public class Unstack {
@Param(0)
private String block1;
@Param(1)
private String block2;
[...]
}
class PickUp(Predicate):
predicateName="pick-up"
def __init__(self, block=None):
super(PickUp, self).__init__([("block")])
self.block = block
[...]
class PutDown (Predicate):
predicateName="put-down"
def __init__(self, block=None):
super(PutDown, self).__init__([("block")])
self.block = block
[...]
class Stack (Predicate):
predicateName="stack"
def __init__(self, block1=None, block2=None):
super(Stack, self).__init__([("block1"), ("block2")])
self.block1 = block1
self.block2 = block2
[...]
class Unstack (Predicate):
predicateName="unstack"
def __init__(self, block1=None, block2=None):
super(Unstack, self).__init__([("block1"), ("block2")])
self.block1 = block1
self.block2 = block2
[...]
At this point, supposing that we are given two files defining the blocks-world domain and a problem instance, we can start deploying our application:
public class Blocksworld {
private static String domainFileName = "domain.pddl";
private static String problemFileName = "p01.pddl";;
public static void main(String[] args) {
Handler handler = new DesktopHandler(new SPDDesktopService());
final InputProgram inputProgramDomain = new PDDLInputProgram(PDDLProgramType.DOMAIN);
inputProgramDomain.addFilesPath(domainFileName);
final InputProgram inputProgramProblem = new PDDLInputProgram(PDDLProgramType.PROBLEM);
inputProgramProblem.addFilesPath(problemFileName);
handler.addProgram(inputProgramDomain);
handler.addProgram(inputProgramProblem);
try {
PDDLMapper.getInstance().registerClass(PickUp.class);
PDDLMapper.getInstance().registerClass(PutDown.class);
PDDLMapper.getInstance().registerClass(Stack.class);
PDDLMapper.getInstance().registerClass(Unstack.class);
Plan plan = (Plan)(handler.startSync());
for (final Object obj : plan.getActionsObjects())
//Manage objects as needed
} catch (Exception e) {
// Handle Exception
}
}
}
class Blocksworld():
__domainFileName = "domain.pddl"
__problemFileName = "p01.pddl"
def main(self):
handler = DesktopHandler(SPDDesktopService())
inputProgramDomain = PDDLInputProgram(PDDLProgramType.DOMAIN)
inputProgramDomain.addFilesPath(self.__domainFileName)
inputProgramProblem = PDDLInputProgram(PDDLProgramType.PROBLEM)
inputProgramProblem.addFilesPath(self.__problemFileName)
handler.addProgram(inputProgramDomain)
handler.addProgram(inputProgramProblem)
try:
PDDLMapper.getInstance().registerClass(PickUp)
PDDLMapper.getInstance().registerClass(PutDown)
PDDLMapper.getInstance().registerClass(Stack)
PDDLMapper.getInstance().registerClass(Unstack)
plan = handler.startSync()
for obj in plan.getActionsObjects():
#Manage objects as needed
except:
#Handle Exception
if __name__ == '__main__':
Blocksworld.main()
The class contains an Handler instance as field, that is initialized with a DesktopHandler using the required parameter SPDDesktopService.
Then it's set-up the input to the solver; since PDDL requires separate definitions for domain and problem, two PDDLInputProgram are created and then given to the handler.
The next lines inform the PDDLMapper about what classes are intended to map the output actions.
Finally the solver is invoked, and the output is retrieved. The output actions can be managed accordingly to the user's desiderata.
The framework is released under The MIT License (MIT)
The framework library for DLV on Android embeds the DLV system itself, which is free for academic and non-commercial educational use, as well as for use by non-profit organizations. For further information about DLV, please refer to the DLVSystem Ltd. home page.
DLVfit is a health and fitness app that monitors the user activity during the day and suggests some workout plans that depend on her age, weight, gender and goals. The app periodically stores some information about the user activities (running, walking, etc.) and infers the current amount of calories burned so far. When the user asks for a workout plan, DLVfit proposes a set of exercises that would allow the user to reach her daily goal, taking into account also to her preferences. The suggested workout plans are computed in the background by DLV via EmbASP.
The ASP program used within DLVfit can be found in the repository. Basically, the program guesses for possible fitness exercises to do in order to burn the remaing calories. Each answer set represent a possible workout plan, in which is ensured that the user's requirements about the calories to burn and the time to spend in the workout are respected. Moreover, also user's preferences are taken into account by means of weak constraints.
You can download the latest version of DLVfit here.
The app should work on most devices equipped with Android 4.x/5.x.
The list of compatible devices include (but it is not limited to):
GuessAndCheckers is a native mobile application that works as an helper for users that play "live" games of the (Italian) checkers (i.e., by means of physical board and pieces). The app, that runs on Android, can help a player at any time: by means of the device camera a picture of the board is taken, and the information about the current status of the game is properly inferred thanks to OpenCV, an open source computer vision and machine learning software; an ASP-based artificial intelligence module then suggests the move.
Thanks to EmbASP and the use of ASP,
GuessAndCheckers features a fully-declarative approach that made easy to develop
and improve several different strategies, also experimenting with many combinations thereof.
The source code of this application along with the Android Application Package (APK)
are available online.
You can download the latest version of GuessAndCheckers here.
DLVEdu is an educational Android App for children, that integrates well-established mobile technologies, such as voice or drawn text recognition, with the modeling capabilities of ASP. In particular, it is able to guide the child throughout the learning tasks, by proposing a series of educational games, and developing a personalized educational path. The games are divided into four macro-areas: Logic, Numeric-Mathematical, Memory, and Verbal Language. The usage of ASP allows the application to adapt to the game experiences fulfilled by the user, her formative gap, and the obtained improvements.
The application continuously profiles the user by recording mistakes and successes, and dynamically builds and updates a customized educational path along the different games. The application features a "Parent Area", that allows parents to monitor child's achievements and to express some preferences, such as explicit filters of some games or educational areas.
Connect4 is an application that allows a user to play the popular Connect Four game (also
known as Four-in-a-Row) against an ASP-based artificial player.
The Connect Four game is played by two opponents on a vertical 7*6 rectangular board.
At each turn, the players fill the board by dropping 1 disk into one of the column so
that it falls from the top to the lowest unoccupied position in the column. The winner
is the first player who gets four of her disks in a line, connected either horizontally,
vertically, or diagonally.
Different AIs have been implemented, ranging from the most powerful one implementing advanced techniques for the perfect play to the simplest one relying on some classical heuristic strategies. By using EmbASP, two different versions of the same app have been built: one for Android, making use of DLV, and one for Java-enabled desktop platforms, making use of clingo.
LoIDE
is a web-based IDE for Logic Programming.
An extensive documentation can be found
here.
A live beta version can be found online at
https://www.mat.unical.it/calimeri/projects/loide
This online version uses the
ASPServerExecutor to run the ASP solvers (using the EmbASP Framework).