=Paper= {{Paper |id=Vol-3193/short8GDE |storemode=property |title=Embedding s(CASP) in Prolog |pdfUrl=https://ceur-ws.org/Vol-3193/short8GDE.pdf |volume=Vol-3193 |authors=Jan Wielemaker,Mikko Tiihonen |dblpUrl=https://dblp.org/rec/conf/iclp/WielemakerT22 }} ==Embedding s(CASP) in Prolog== https://ceur-ws.org/Vol-3193/short8GDE.pdf
Embedding s(CASP) in Prolog
Jan Wielemaker1,2 , Mikko Tiihonen3
1
  SWI-Prolog Solutions b.v., Amsterdam, The Netherlands
2
  Vrije Universiteit Amsterdam, De Boelelaan 1105, 1081 HV Amsterdam, The Netherlands
3
  Forsante Oy, Helsinki, Finland


                                         Abstract
                                         The original s(CASP) implementation is a stand-alone program implemented in Ciao Prolog. It reads
                                         the s(CASP) source using a dedicated parser, resolves the query embedded in the source and emits the
                                         results in a format dictated by commandline options. Typical applications require composing a s(CASP)
                                         program, solving a query and reasoning about the bindings, model and/or justification. This is often
                                         done in some external language, e.g., Python. In this paper we propose a closer integration with Prolog.
                                         The s(CASP) program is simply a Prolog program that has to respect certain constraints. The scasp
                                         library can be used to solve a query using s(CASP) semantics, making the bindings available in the
                                         normal Prolog way and providing access to the model and justification as Prolog terms. This way, we
                                         can exploits Prolog’s power for manipulating (Prolog) terms for construction the s(CASP) program and
                                         interpreting the results.

                                         Keywords
                                         s(CASP), answer set programming, multi-paradigm, Prolog,




1. Introduction
ASP, s(ASP) and s(CASP) [1, 2, 3] are languages that deal with knowledge representation and
reasoning. Lacking (easily) predictable ordering of resolution steps and facilities for input and
output, these languages are not general purpose programming languages. This implies that for
deployment in real-world applications we need a program written in a general purpose language
to prepare the input for the s(CASP) solver and interpret the output. Traditionally the solver is
written as a stand alone application or as a library that is connected to a scripting language such
as Lua or Python. While these scripting languages have excellent infrastructure for interfacing
with typical ICT infrastructure, they are poorly equipped for manipulating Prolog terms, in
particular when these contain logical variables, optionally with constraints.
   In contrast, Prolog is perfectly equipped for manipulating s(CASP) programs and the result
of solving an s(CASP) query. This is already sufficient reason to use Prolog as s(CASP) scripting
language for mostly self-contained tasks such as performing ILP (Inductive Logic Programming)
or similar machine learning tasks using s(CASP). In addition, SWI-Prolog [4] provides a rich
set of libraries to interface with ICT infrastructure. Examples are its HTTP server framework,
STOMP and Redis interfaces and its I/O libraries for XML, HTML, RDF, JSON, YAML and more.

GDE’22
$ jan@swi-prolog.com (J. Wielemaker); mikko.tiihonen@forsante.com (M. Tiihonen)
 0000-0001-5574-5673 (J. Wielemaker); 0000-0001-8773-5672 (M. Tiihonen)
                                       © 2022 Copyright for this paper by its authors. Use permitted under Creative Commons License Attribution 4.0 International (CC BY 4.0).
    CEUR
    Workshop
    Proceedings
                  http://ceur-ws.org
                  ISSN 1613-0073       CEUR Workshop Proceedings (CEUR-WS.org)
Together with SWI-Prolog’s scalable multi-threading this platform can provide scalable s(CASP)
based services with pre- and postprocessing in systems based on a micro service architecture.
A SWI-Prolog based s(CASP) reasoner can also be embedded in C++, Java, Python, Rust and
several other languages.
   This paper describes the result of embedding s(CASP) in SWI-Prolog as part of a commercial
project applying s(CASP) at Forsante Oy for reasoning about medical guidelines. This paper
first discusses the requirements for embedding s(CASP) in Prolog. In section 3 we discuss two
designs, our initial design, its shortcoming and our final design. Section 4 summarizes the
provided Prolog libraries. The final sections discuss how s(CASP) is made available online
embedded in SWISH, provide an evaluation and conclusions.


2. A closer look at the requirements
We already mentioned Prolog’s advantages for assembling s(CASP) programs and post-
processing the output of the s(CASP) solver. In this section we take a closer look at the
requirements.

Dynamic assembly of a s(CASP) program Dynamically assembling a s(CASP) program is
a requirement for applications that wish to add case data to the program, i.e., the state of the
(relevant) world. This typically concerns facts. In the case of incomplete knowledge we may
explore the consequences of defining some facts as abductive. In addition, dynamic manipulation
of rules is required to explore the search space for machine learning tasks such as ILP (Inductive
Logic Programming).

Tools and program analysis Prolog has a much longer history than s(CASP) when it comes
to tools for syntax checking, dependency analysis, etc. Our integration should preserve these
features as much as possible.

Modularity of s(CASP) An application may involve multiple s(CASP) programs that may
use each other. This implies we must be able to represent s(CASP) programs as units and allow
a unit to use other units.

Modular access s(CASP) applications may want to have access at different levels to the results.
Some programs want to reason about the model and justification and need a clean representation
thereof as Prolog terms. Interactive programs aiming at experts need an integration into the
Prolog toplevel that represent the model and justification in a concise and easy to read format.
Finally, end users typically want a natural language description of the findings and justification.
3. Choosing a suitable embedding
3.1. Our initial solution: blocks
In our invited talk at GDE-2021 [5] we proposed a solution that stayed as close as possible to a
s(CASP) program as represented in a file. A minimal example is below.

:- use_module(library(scasp/embed)).
:- begin_scasp(qp, [p/1]).
p(X) :- not q(X).
p(1).
q(X) :- not p(X).
:- end_scasp.

Using begin_scasp/2 . . . end_scasp/0 we established the two steps of the embedding process.
Firstly the opening directive allows adjusting the Prolog syntax by means of (re)defining
operators and other flags. Below are some examples of statements that are legal in the original
s(CASP) implementation and illegal or ambiguous in Prolog.1

    • #abducible penguin(X).
    • _p :- q.
    • :- p,q.
    • ?- q(X).

  While reading the s(CASP) block a term_expansion/2 rule is active that validates that all
input is valid s(CASP) input. The input is collected. The expansion of end_scasp/0 calls the
s(CASP) compiler to generate the program.

3.1.1. Issues using s(CASP) blocks
While the above solution stays as close as possible to using s(CASP) as loaded from a separate
file and the compilation of the s(CASP) program is done at Prolog compile time, this approach
has several disadvantages.

    • The strongly altered Prolog syntax confuses the Prolog support of many editors.
    • The syntactical changes make it harder to manipulate s(CASP) programs. We ended up
      with a predicate that could add a complete block dynamically. To assemble the content of
      this block we need to enable the syntax modifications.
    • We need specialised code to import other s(CASP) units.
    • We cannot use any normal Prolog convention to analyse, inspect or modify the program.




   1
       _p :- q. can be made legal in SWI-Prolog using the Prolog flag allow_variable_name_as_functor.
3.2. Our final solution: dynamic reinterpretation of Prolog
The above limitations made us reconsider our initial choice to embed s(CASP) in isolated blocks.
In our current approach we allow s(CASP) reasoning over a (restricted) Prolog program. The
advantages of this approach are obvious: we can use all normal tools and Prolog interfaces to
manage s(CASP) programs. Think about, assert/retract, modules, cross-referencing, etc.
   This approach has some consequences. Firstly, we need to define replacements for some
of the syntactical problems. Second, given a query we need to collect the relevant s(CASP)
program, compile the program, solve the query using s(CASP) semantics and dispose of the
program. Below we have a closer look at some of the consequences.

Syntax    We took the following decisions about the syntax:

    • Predicates starting with an underscore (_p(X)) are not supported without quotes (i.e.,
      ’_p’(X)) is allowed.
    • s(CASP) directives are written as normal Prolog directives, e.g., :- abducible p(_X).
      abducible/1 is defined as a predicate that may also be called at runtime. It asserts an
      annotation that is picked up by the s(CASP) compiler.
    • Global constraints are written as false :- Body. to avoid conflicting with Prolog
      directives.
    • Classic negation is still written as -p :- Body. The - prefix for a clause head or goal is
      handled by term_expansion/2 and goal_expansion/2 to include the - into the name.

Modifying the s(CASP) program Modification of the s(CASP) program is close to the modi-
fication of a normal Prolog program, unless classical negation or global constraints are involved.
For this reason we added scasp_assertz/1 and similar predicates for all database manipulation
predicates. For example, scasp_assertz(-p(1)) is equivalent to assertz(’-p’(1)) Sim-
ilarly some Prolog directives such as discontiguous/1 have as scasp_ counterpart to act on
classical negation.

Collecting the s(CASP) program Fortunately s(CASP) program terms are a simple subset
of Prolog. Due to the lack of higher order constructs such as call/1, analysis of the call
graph is straightforward. The analysis does however require introspection capabilities to
all relevant clauses. SWI-Prolog allows for clause/2 to operate on static predicates which
allows for inspecting static Prolog programs. Some other implementations (e.g., Quintus) allow
loading programs as all dynamic, providing the same features. In other systems one may use
term_expansion/2 to save a copy that can be inspected.
   Given clause/2 (or some alternative) the analysis starts with the query, fetches all clauses
relevant to the query and then recursively all clauses relevant to the body of the collected
clauses. This process continues until no new clauses can be found. While doing so each clause
is inspected not to have any forbidden constructs such as foreign predicates, control structures
except for conjunction or meta-calling. The translation aborts with an error if such a clause is
found.
   Global constraints are a separate problem. As said, they are rules for false/0, which is
translated during program loading to rules for - /0 to avoid a conflict with the ISO builtin
false/0. s(CASP) semantics define that all global constraints of the program shall be included
in the translation and evaluation. This is impractical as we no longer have blocks and thus do
not know which global constraints belong to which s(CASP) program. Therefore we extend
the goal directed properties of s(CASP). We include a global constraint and its call tree if (and
only if) the call tree of the body of the global constraint contains a literal that overlaps with
the call tree of the current s(CASP) program. We compare two literals for overlapping after
normalization by removing not/1 and classical negation. This process is applied until there
remain no further global constraints whose call tree overlaps with the current program.
   Next, we collect all annotations created by s(CASP) directives.
   As the original s(CASP) compiler does not support modules we flatten the module
qualified program by including the module name into the name of the predicates, e.g.
’rules:p’(X) :- ’facts:f’(X), ’facts:g’(X). As a result, s(CASP) programs are not
allowed to have the : character in their name.
   The result is passed to the barely modified original s(CASP) compiler to create the dual, OLON
and nmr rules. These are stored in a SWI-Prolog temporary module. The module is created
and destroyed using setup_call_cleanup/3, where the setup handler creates the module and
performs the s(CASP) compilation as described above, the call handler runs scasp/2 and the
cleanup handler destroys the module and its content.

Executing a s(CASP) query The principal predicate for evaluating a s(CASP) query is
scasp/2, which takes a query and an option list. The option list represents many the s(CASP)
commandline options (such as the forall algorithm, enabling warnings, disabling OLON rules,
etc. In addition it has an option model(Model) and tree(Tree) that may be used to get access to
the complete model and justification tree.
   The Model is a list of Prolog terms, optionally embedded in not/1 or -/1, e.g. not(-(p))
means the classical negation of p/0 could not be proven. The model is ordered by the literal
that is involved, i.e. all model terms that belong to a specific literal are adjacent. Variables in
the model may have constraints that are accessible using the Prolog built-in copy_term/3.
   The justification Tree is a Prolog term of the shape Node - Children. Here, Node has the same
representation as a model term and Children is a list of nodes, possibly embedded in chs/1
(assumed in even loop) or proved/1 (already proved earlier in the justification).
   For toplevel (REPL) interaction we built upon ?/1 that was provided by the original imple-
mentation. We introduced nine predicates with the names ?/1, ??/1, ?–/1, ?+-/1, ?-+/1, ?++/1,
??+-/1, ??-+/1 and ??++/1. Predicates with a single ? display their results in formal notation
using Unicode characters for logical connectors. Predicates with a double ? (??) display their
results as natural language. The first ± specifies whether the model is displayed and the second
± wheter the justification is displayed. ?/1 is an alias for ?+-/1 and ??/1 is an alias for ??++/1 for
compatibility with the original s(CASP) implementation. If supported by the terminal, both use
colour output using green for positive literals, orange for NAF and red for classical negation.
See figure 1
Figure 1: REPL output of query showing tabular output, Unicode logical connectors, colours to indicate
connectors and the truth of literals. Remaining constraints are displayed below the model.


4. s(CASP) as a modular Prolog library
The SWI-Prolog s(CASP) system has several libraries for public usage.

library(scasp) This library is the core for embedded usage. It exports the s(CASP) operators,
expansion rules, s(CASP) directives, predicates to inspect and modify the s(CASP) program and
predicates to run s(CASP) queries.

library(scasp/html) This library exports predicates to translate a justification tree to an
HTML string. The output contains both the formal and natural language output. The system
provides JavaScript and CSS to switch between the formal and natural language output and to
fold and unfold the justification tree. The library provides partial support for multiple languages.
The translation is based on the work on the original s(CASP) implementation [6].

library(scasp/human) This library exports predicates to translate a justification tree into a
natural language string. This library uses the rules from library(scasp/html) such that we can
use one set of translation rules for both HTML and plain text output.

library(scasp/main)             This library provides the code for creating the stand-alone executable.


5. s(CASP) online using SWISH
SWISH [7] is an online version of SWI-Prolog where multiple users can connect to a SWI-Prolog
server process to edit, store and execute Prolog programs. The programs are executed on the
server. When a user executes a query on a program in the browser, the program is transferred to
the server, loaded into a temporary module (see page 5), and the query is executed. The public
SWISH instance is popular for education. It currently (May 2022) stores 151,164 programs and
is on average used by a couple of hundreds of (mostly) students.2
    2
        Usage follows a daily, weekly and (academic) year cycle, ranging between about 50 and 1,200 concurrent users).
Figure 2: s(CASP) embedded in a SWISH notebook. The output field contains the binding, the model in
natural language notation and the justification in formal language. This example is created by Jason
Morris and available in the public SWISH instance [8]


  Given the embedding in Prolog, we can load library(scasp) in SWISH and run s(CASP) queries
online. A brief tutorial is provided. Figure 2 illustrates the interface.


6. Evaluation
We used the above integration to realise the scasp embedding into SWISH. We use s(CASP)
at Forsante Oy where it will operate as a micro service using the STOMP protocol for com-
munication. Finally we implemented a simple HTTP service for s(CASP) that is available as
examples/dyncall/http.pl from the SWI-Prolog s(CASP) distribution. Currently (May
2022) SWISH stores 64 programs that match on a search for library(scasp).3 The SWI-Prolog
forum collected 41 topics that mentions s(CASP), 22 of which also mention SWISH.
   As Forsante Oy we used dynamic manipulation of the s(CASP) program to reduce the number
of variables introduced in the body of clauses. Such variables introduce forall reasoning that
proved to be a major performance bottleneck. For example, we have facts of the format
measurement(What, Unit, Value) and rules that depend whether some measurement has
been performed. Such rules may be written down as below. Instead, we add facts measured/1
derived from all measurement/3 facts.

measured(What) :- measurement(What,_,_).

    3
    Note that it is not needed to store a program on the server. The SWISH web page stores your programs in the
browser’s local storage.
Although we have not performed a systematic analysis of the cost for dynamically collecting
and compiling the s(CASP) for each query this part of the evaluation never came above a few
percent of the total s(CASP) evaluation time according to SWI-Prolog’s profiling tools.


7. Conclusions and future work
Embedding s(CASP) in Prolog by evaluating a normal Prolog program using s(CASP) semantics
by dynamically collecting, compiling and evaluating the s(CASP) query proved to be a powerful
technique. This way of embedding is supported by the established Prolog development toolchain.
All normal Prolog features for loading, modifying, introspection and modularity are supported.
To deal naturally with classical negation and global constraints we need some preprocessing
while loading such programs as well as wrappers around the Prolog database predicates, e.g.,
scasp_assert/1/.
   We consider the embedding successful. Future versions may incorporate minor changes to
the interface. Further work on s(CASP) will concentrate on better natural language output with
support for multiple languages. Other areas of interest are performance and testing.


References
[1] V. Lifschitz, Answer set planning, in: D. D. Schreye (Ed.), Logic Programming: The 1999
    International Conference, Las Cruces, New Mexico, USA, November 29 - December 4, 1999,
    MIT Press, 1999, pp. 23–37.
[2] K. Marple, G. Gupta, Dynamic consistency checking in goal-directed answer set pro-
    gramming, Theory Pract. Log. Program. 14 (2014) 415–427. URL: https://doi.org/10.1017/
    S1471068414000118.
[3] J. Arias, M. Carro, E. Salazar, K. Marple, G. Gupta, Constraint answer set programming
    without grounding, Theory Pract. Log. Program. 18 (2018) 337–354. URL: https://doi.org/10.
    1017/S1471068418000285. doi:10.1017/S1471068418000285.
[4] J. Wielemaker, T. Schrijvers, M. Triska, T. Lager, Swi-prolog, Theory Pract. Log. Program.
    12 (2012) 67–96. URL: https://doi.org/10.1017/S1471068411000494.
[5] J. Wielemaker, J. Arias, G. Gupta, s(CASP) for SWI-Prolog, in: J. A. et al. (Ed.), Proceedings
    of the ICLP 2021 Workshops, Porto, Portugal (virtual), September 20th-21st, 2021, volume
    2970 of CEUR Workshop Proceedings, CEUR-WS.org, 2021. URL: http://ceur-ws.org/Vol-2970/
    gdeinvited4.pdf.
[6] J. Arias, M. Carro, Z. Chen, G. Gupta, Justifications for goal-directed constraint answer set
    programming, in: F. R. et al. (Ed.), Proceedings 36th ICLP Technical Communications 2020,
    UNICAL, Rende (CS), Italy, 18-24th September 2020, volume 325 of EPTCS, 2020, pp. 59–72.
    URL: https://doi.org/10.4204/EPTCS.325.12. doi:10.4204/EPTCS.325.12.
[7] J. Wielemaker, T. Lager, F. Riguzzi, SWISH: swi-prolog for sharing, CoRR abs/1511.00915
    (2015). URL: http://arxiv.org/abs/1511.00915. arXiv:1511.00915.
[8] J. Morris, Constraint Answer Set Programming as a Tool to Improve Legislative Drafting:
    A Rules as Code Experiment, Association for Computing Machinery, New York, NY, USA,
    2021, p. 262–263. URL: https://doi-org.vu-nl.idm.oclc.org/10.1145/3462757.3466084.