Computational Logic:
(Constraint) Logic Programming
Theory, practice, and implementation
Abstract Specialization
and its Applications
 Many techniques exist for program specialization which provide
useful optimizations:
 Partial Evaluation has received considerable attention and has
been proved of interest in many contexts.
 Supercompilation is another very powerful technique for program
specialization.
 Abstract Specialization (the topic of this talk!)
 Its main features are
 the knowledge which is exploited is the socalled
static data, which corresponds to (partial) knowledge at
specialization (compile) time about the input data.
 Data which is
not known at specialization time is called dynamic.
 The program is optimized by performing at specialization time
those parts of the program execution which only depend on static
data.
 Has been applied to many programming languages and paradigms
 Two basic approaches have been addressed:
 Online techniques
 Offline techniques
 We have proposed Abstract Specialization, whose main ingredients
are:
 Abstract Interpretation for
 allowing abstract information
 performing fixpoint computations for computing (safe
approximations of) success substitutions.
 Abstract Executability for optimizing program literals
 Using the program induced by polyvariant abstract interpretation
 Minimizing the number of versions
 It is a generic technique. Different abstract domains can be
used which allow different information and optimizations.
 All four ingredients above are essential. Our system provides
the first practical implementation of a system with all the features
above.
 Abstract interpretation obtains information about the
runtime behavior of the program by
 simulating the execution using an abstract
domain which is simpler than the concrete one,
 performing fixpoint computations for recursive calls.
 An abstract domain is the set of all possible
abstract semantic values.
 Values in the abstract domain are finite representations of a,
possibly infinite, set of values in the concrete domain ().
 and are related via a pair of monotonic
mappings
:
 abstraction
 concretization
such that:
, and
.
i.e.,
conform a Galois insertion over and .
 Consider the Prolog program below
plus1(X,Y): ground(X), Y is X + 1.
plus1(X,Y): var(X), X is Y  1.
 It defines the relation that the second argument is the first
argument plus one.
 It can be used when at least one of its arguments is
instantiated.
 Example executions:
 The call plus1(5, Result) computes the addition of 1 and
5 and assigns it to Result .
 The call plus1(Num, 3) determines which is the number
Num such that when added to 1 returns 3 (i.e., Num
= 3  1).
 The call plus1(3, 8) fails.
 Suppose we want to specialize the program below for the
initial query ? p(2,Res).
p(Value,Res): Tmp is Value * 3, plus1(Tmp,Res).
plus1(X,Y): ground(X), Y is X + 1.
plus1(X,Y): var(X), X is Y  1.
 Using an appropriate unfolding rule, a partial evaluator would
compute the following specialized program:
p(2,7).
in which all computation has been performed at analysis time.
 Imagine we want to specialize the same program (repeated below)
but now for the initial query ? p(Value,Res).
p(Value,Res): Tmp is Value * 3, plus1(Tmp,Res).
plus1(X,Y): ground(X), Y is X + 1.
plus1(X,Y): var(X), X is Y  1.
 Since Value is dynamic, we cannot compute Tmp
at specialization time.
 As a result, the call plus1(Tmp,Res) cannot be
optimized.
 Using a sensible unfolding rule, the specialized program
corresponds to the original one.
p(Value,Res) :
true(( term(Value), term(Res), term(Tmp) )),
Tmp is Value * 3,
true(( arithexpression(Value), term(Res), num(Tmp) )),
plus1(Tmp,Res),
true((arithexpression(Value), arithexpression(Res),num(Tmp))).
plus1(X,Y) :
true(( num(X), term(Y) )),
ground(X),
true(( num(X), term(Y) )),
Y is X+1,
true(( num(X), num(Y) )).
plus1(X,Y) :
true(( num(X), term(Y) )),
var(X),
true(( num(X), term(Y) )),
X is Y1,
true(( num(X), arithexpression(Y) )).
 The optimization above is based on abstract execution
 A literal in a program can be reduced to
the value
 true if its execution can be guaranteed to succeed only
once with the empty computed answer
 false if its execution can be guaranteed to finitely
fail.
 Consider program below with query ? p(Res). and
plus1/2 is defined as usual. Note that the execution tree
of even/1 is infinite.
p(Res): even(Tmp), plus1(Tmp,Res).
even(0).
even(E): even(E1), E is E1 + 2.
 In order to compute some approximation of the success
substitution for even(Tmp) a fixpoint computation is
required.
 The information can be used to optimize the program as follows:
p(Res): even(Tmp), Res is Tmp + 1.
even(0).
even(E): even(E1), E is E1 + 2.
 The approach just shown is simple and can achieve relevant
optimizations
 However, opportunities for optimization quickly disappear if
polyvariance is not used
 Consider the following program:
p(Value,Res):
plus1(Tmp, Value), Tmp2 is Tmp * 3, plus1(Tmp2,Res).
 note that two different calls for plus1/2 appear.
 The analysis information we now get is:
plus1(X,Y) :
true(( term(X), term(Y) )),
ground(X),
true(( term(X), term(Y) )),
Y is X+1,
true(( arithexpression(X), num(Y) )).
plus1(X,Y) :
true(( term(X), term(Y) )),
var(X),
true(( term(X), term(Y) )),
X is Y1,
true(( num(X), arithexpression(Y) )).
 which no longer allows abstractly executing either of the two
tests.
 This can be solved by having two separate versions for predicate plus/3.
p(Value,Res):
plus1_1(Tmp, Value), Tmp2 is Tmp + 3, plus1_2(Tmp2,Res).
plus1_1(X,Y): ground(X), Y is X + 1.
plus1_1(X,Y): var(X), X is Y  1.
plus1_2(X,Y): ground(X), Y is X + 1.
plus1_2(X,Y): var(X), X is Y  1.
 which allows optimizing each version separately as
plus1_1(X,Y) : X is Y1.
plus1_2(X,Y) : Y is X+1.
 And even unfolding the calls to plus1/2 in the first
clause, resulting in:
p(Value,Res) :
Tmp is Value 1, Tmp2 is Tmp+3, Res is Tmp2 + 1.
 Though in the example above it seems relatively easy,
 It is not straightforward in general to decide when to generate
additional versions.
 This problem is often referred to as ``Controlling
Polyvariance''.
 It has received considerable attention in different contexts:
program analysis, partial evaluation, etc.
 We have to enable as many optimizations as possible, but we have
to avoid:
 codeexplosion
 or even nontermination!
 Naive heuristics: generate one version per call to
the predicate.
 Consider the following program:
p(Value,Res):
plus1(Tmp, Value), plus1(Tmp,Tmp1), plus1(Tmp1,Tmp2),
plus1(Tmp2,Tmp3), plus1(Tmp3,Res).
 By generating multiple versions of plus1/2 we can
optimize them. Otherwise it is not possible.
 However, the expanded program would have 5 different
versions for plus1/2, when in fact only two of them are
needed.
 This shows that we have to avoid generating spurious versions.
 Consider the following program:
p(Value,Res): q(Tmp, Value), Tmp2 is Tmp + 3, q(Tmp2,Res).
q(A,B):
% may do other things as well
other(A,B), plus1(A,B).
other(A,B): write(A), write(B).
 By using the naive approach only one version of plus1/2
will be generated. As a result, it cannot be optimized.
 The solution lies in generating two versions of q/2 in
order to create a ``path'' to the now separately specialized versions.
 This shows that we may need more versions than calls in the program.
 The expanded program is shown below:
p(Value,Res):
q_1(Tmp, Value), Tmp2 is Tmp + 3, q_2(Tmp2,Res).
q_1(A,B): other(A,B), plus1_1(A,B).
q_2(A,B): other(A,B), plus1_2(A,B).
 This program can now be optimized to:
p(Value,Res) :
q_1(Tmp,Value), Tmp2 is Tmp+3, q_2(Tmp2,Res).
q_1(A,B) : other(A,B), A is B  1.
q_2(A,B) : other(A,B), B is A + 1.
<<580>>
 GoalDependent abstract interpretation aims at computing
both call and success abstract patterns
 In order to achieve as much accuracy as possible, most
(goaldependent) abstract interpreters are polyvariant.
 Different call patterns for the same procedure are treated
separately.
 Different levels of polyvariance can be used with different
degrees of accuracy and efficiency (tradeoff)
 In spite of analysis being polyvariant, the usual approach is to
``flatten'' the information corresponding to different versions
prior to using it to optimize the program.
 Many of the existing abstract interpreters for logic programs
are based on an andor tree semantics a la Bruynooghe.
 There, analysis builds an andor graph in which:
 ornodes represent atoms (procedure calls). They are
adorned with a call and a success substitution.
 andnodes represent clauses and are adorned with the
head of the clause they correspond to.
 The andor graph is often not explicitly computed for
efficiency reasons.
 Thus, most optimizing compilers do not use the full andor graph
but rather a description of procedures as call and success pairs.
 This eliminates multiple specialization, losing opportunities for
optimization.
 Since polyvariant analysis considers several versions of
procedures, this in effect corresponds to a polyvariant program:
 Each pair (procedure, call pattern) corresponds to a different
version.
 The call pattern for each literal uniquely determines the
version to use.
 Thus, the ``polyvariant'' program can be materialized.
 Each version is implemented with a different procedure name
 Renaming is performed so that no runtime overhead is incurred
to select versions.
 Even for relatively small programs, the number of versions
generated by analysis can be very large.
 It does not depend on whether the additional versions lead to
additional optimizations or not.
 Our systems includes a minimizing algorithm which guarantees that:
 the resulting program is minimal
 while not losing any opportunities for specialization
 In our experience, using the minimization algorithm the size of
the polyvariant program increases w.r.t. the original one, but does
not explode.
 Other possibilities need to be explored.
<<609>>
<605>>
 Parallelization process starts with
dependency graph:
 edges exist if there can be a dependency,
 conditions label edges if the dependency can be removed.
 Global analysis:
reduce number of checks in conditions (also to true and false).
 Annotation: encoding of parallelism in the target parallel
language:
g(...), g(...), g(...)
figure=par_process_wide.ps,height=0.4
 Example:
qs([XL],R) : part(L,X,L1,L2),
qs(L2,R2), qs(L1,R1),
app(R1,[XR2],R).
Might be annotated in &Prolog (or Ciao Prolog), using local analysis, as:
qs([XL],R) :
part(L,X,L1,L2),
( indep(L1,L2) >
qs(L2,R2) & qs(L1,R1)
; qs(L2,R2) , qs(L1,R1) ),
app(R1,[XR2],R).
Global analysis would eliminate the indep(L1,L2) check.
figure=/home/clip/Slides/nmsu_lectures/par/Figs/apcompiler.ps,width=0.7
<583>>
 Dynamic scheduling allows dynamically ``delaying'' some calls
until their arguments are sufficiently instantiated.
 Dynamic scheduling increases the expressive power of
programs but it also has some time and space overhead.
 We aim at reducing as much as possible this additional overhead,
while preserving the semantics of the original program.
 We present two classes of program transformations:
 simplification of delay conditions.
 reordering delaying literals later in their rule body.
 Our aim is not to produce an order for the literals in a clause
such that dynamic scheduling is not needed anymore.
 Our aim is to reorder literals when:
 it does not alter the program semantics (search space)
 it reduces scheduling cost
 The search space of the reordered program may be larger than
that of the original program.
 At the limit, reordering may transform a finite, successful
computation into an infinite failure.
 Reordering a literal which definitely delays at the point where it is
may introduce an unbounded amount of slowdown.
 Consider the following example program and the query
? delay_until(ground(Y),p(Y)), q(Y,Z).
q(Y,Z) : Y=2, long_computation(Z).
q(Y,Z) : Y=3, Z=5.
p(Y) : Y=3.
 p(Y) delays in its position in the query.
 If we reorder the query, p(Y) does not delay after q(Y,Z). We can eliminate its delay condition.
 The reordered query ? q(Y,Z), p(Y) performs the
long_computation while the original query does not.
 In online PE propagation of values and program transformation
are performed simultaneously.
 Done by means of unfolding, which has two effects:
 transforms the program,
 propagates the values of variables computed during the
transformation to other atoms in the node.
 Propagation can only be achieved through unfolding but:
 Unfolding is not always a good idea.
 Unfolding is not always possible.
The SLD tree may be infinite and we have to stop at some point.
 As soon as we stop unfolding, separate SLD trees are required.
 Thus, for most programs, several SLD trees exist.
 However, in PE there is no propagation between separate SLD trees.
 In summary, we would like to improve the propagation capabilities
of PE by allowing:
 propagation without transformation,
 propagation among different SLD trees (residual atoms).
 The advantages of AndOr graphs w.r.t. SLD trees are:
 Propagation does not require transformation. Success
substitutions are computed all over the graph.
 Even infinite computations are approximated using fixpoint
computations.
 There is only one global graph and propagation among different
atoms is possible and natural.
 In addition to concrete values, it can also work with other
interesting properties: types, modes, aliasing, etc.
 The advantages of SLD trees w.r.t. AndOr graphs are:
 Simpler to build. No abstract domain, no fixpoint required.
 Codegeneration is straightforward.
 In order to integrate PE into AI we need
 an abstract domain (and widening operator) which captures concrete values,
 an algorithm for code generation from the andor graph,
 a way of performing unfolding steps when profitable.
 The first two points are fully achieved.
 Several alternatives exist for the third one (see the paper).
 The currently implemented approach is very simple.
 More powerful unfolding mechanisms have to be tested.
<584>>
 From a partial evaluation perspective, both in logic
[GallagherCS88,Gallagher92] and functional programming [ConselK93].
 From an abstract interpretation perspective in [GiannottiH91] and
the first complete framework in [Winsborough92]. The first
implementation and evaluation in [PueblaH95].
 The drawbacks of PE for propagation are identified in
[LeuschelS96] and a study of advantages of the integration
presented in [Jones97].
 A first integration of AI and PE can be found in[PueblaHG97]
 An alternative formulation can be found in [Leuschel98]. Uses
the notions of Abstract Unfolding and Abstract
resolution.
 A general framework for the integration of abstract
interpretation and program transformation can be found in
[Cousot02].
 Use Abstract Specialization for compiletime assertion
checking.
 Use Abstract Specialization as a front end for an optimizing
compiler in the Ciao system.
 Further explore the application of Abstract Specialization for
Partial Evaluation tasks.
 Use our abstract interpreters for performing Binding Time
Analysis for offline partial evaluation.
 The ASAP IST200138059 FET project: Advanced
Specialization and Analysis for Pervasive Computing.
 In collaboration with Southampton and Bristol (UK) and Roskilde
(DK).
 Basic idea: use automated techniques for reusing
existing code in order to deploy it in resourcebounded systems.
 Program specialization has to be ``resourceaware''.
 The costbenefit relation of optimizations has to be taken into
account.
 First result: An integrated system with the most advanced
specialization and analysis techniques in the (C)LP community.
Feature 
PE 
AS 
Information captured 
concrete 
abstract 
Propagation of information 
unfolding 
abstract interpretation 
Polyvariance control 
global control 
abstract domain 
Guaranteeing termination 
generalization 
widening 
Version selection 
renaming 
renaming 
Materializing optimizations 
unfolding 
abstract executability 
Fixpoint computations 
no 
yes 
Propagation among atoms 
no 
yes 
Infinite failure 
non observable 
observable 
Low level optimization 
nonapplicable 
applicable 
Minimization of versions 
no/yes 
yes 
 Abstract Specialization is generic. Can be used for many
different kinds of information and optimization.
 Propagation of information is independent from performing the
optimizations
 Not restricted to source to source optimizations:
 abstract specialization generates the expanded program
 which can then be combined with a low level optimizer
 It allows achieving an appropriate level of polyvariance
 minimization allows reducing unnecessary polyvariance much
 this allows ``tentative'' polyvariance which can be backtracked.
 A fully fledged integration of traditional partial evaluation
and abstract specialization is most promising.
 1

F. Bueno, D. Cabeza, M. Carro, M. Hermenegildo, P. LópezGarcía, and
G. Puebla.
The Ciao Prolog System. Reference Manual.
The Ciao System Documentation SeriesTR CLIP3/97.1, School of
Computer Science, Technical University of Madrid (UPM), August 1997.
System and online version of the manual available at
http://clip.dia.fi.upm.es/Software/Ciao/.
 2

F. Bueno, D. Cabeza, M. Hermenegildo, and G. Puebla.
Global Analysis of Standard Prolog Programs.
In European Symposium on Programming, number 1058 in LNCS,
pages 108124, Sweden, April 1996. SpringerVerlag.
 3

F. Bueno, M. García de la Banda, M. Hermenegildo, K. Marriott, G. Puebla,
and P. Stuckey.
A Model for Intermodule Analysis and Optimizing
Compilation.
In Logicbased Program Synthesis and Transformation, number
2042 in LNCS, pages 86102. SpringerVerlag, March 2001.
 4

M. Hermenegildo, G. Puebla, and F. Bueno.
Using Global Analysis, Partial Specifications, and an
Extensible Assertion Language for Program Validation and
Debugging.
In K. R. Apt, V. Marek, M. Truszczynski, and D. S. Warren, editors,
The Logic Programming Paradigm: a 25Year Perspective,
pages 161192. SpringerVerlag, July 1999.
 5

M. Hermenegildo, G. Puebla, F. Bueno, and P. LópezGarcía.
Abstract Verification and Debugging of Constraint Logic
Programs.
In Recent Advances in Constraints, number 2627 in LNCS, pages
114. SpringerVerlag, January 2003.
 6

M. Hermenegildo, G. Puebla, F. Bueno, and P. LópezGarcía.
Program Development Using Abstract Interpretation (and
The Ciao System Preprocessor).
In 10th International Static Analysis Symposium
(SAS'03), number 2694 in LNCS, pages 127152. SpringerVerlag, June 2003.
 7

M. Hermenegildo, G. Puebla, K. Marriott, and P. Stuckey.
Incremental Analysis of Logic Programs.
In International Conference on Logic Programming, pages
797811. MIT Press, June 1995.
 8

G. Puebla, F. Bueno, and M. Hermenegildo.
A Generic Preprocessor for Program Validation and
Debugging.
In P. Deransart, M. Hermenegildo, and J. Maluszynski, editors, Analysis and Visualization Tools for Constraint Programming,
number 1870 in LNCS, pages 63107. SpringerVerlag, September 2000.
 9

G. Puebla, F. Bueno, and M. Hermenegildo.
An Assertion Language for Constraint Logic Programs.
In P. Deransart, M. Hermenegildo, and J. Maluszynski, editors, Analysis and Visualization Tools for Constraint Programming,
number 1870 in LNCS, pages 2361. SpringerVerlag, September 2000.
 10

G. Puebla, F. Bueno, and M. Hermenegildo.
Combined Static and Dynamic AssertionBased Debugging of
Constraint Logic Programs.
In Logicbased Program Synthesis and Transformation
(LOPSTR'99), number 1817 in LNCS, pages 273292. SpringerVerlag, March
2000.
 11

G. Puebla, M. García de la Banda, K. Marriott, and P. Stuckey.
Optimization of Logic Programs with Dynamic Scheduling.
In 1997 International Conference on Logic Programming, pages
93107, Cambridge, MA, June 1997. MIT Press.
 12

G. Puebla, J. Gallagher, and M. Hermenegildo.
Towards Integrating Partial Evaluation in a Specialization
Framework based on Generic Abstract Interpretation.
In M. Leuschel, editor, Proceedings of the ILPS'97 Workshop on
Specialization of Declarative Programs, October 1997.
Post ILPS'97 Workshop.
 13

G. Puebla and M. Hermenegildo.
Implementation of Multiple Specialization in Logic
Programs.
In Proc. ACM SIGPLAN Symposium on Partial Evaluation and
Semantics Based Program Manipulation, pages 7787. ACM Press, June
1995.
 14

G. Puebla and M. Hermenegildo.
Automatic Optimization of Dynamic Scheduling in Logic
Programs.
In Programming Languages: Implementation, Logics, and Programs,
number 1140 in LNCS, Aachen, Germany, September 1996. SpringerVerlag.
Poster abstract.
 15

G. Puebla and M. Hermenegildo.
Abstract Specialization and its Application to Program
Parallelization.
In J. Gallagher, editor, Logic Program Synthesis and
Transformation, number 1207 in LNCS, pages 169186. SpringerVerlag, 1997.
 16

G. Puebla and M. Hermenegildo.
Abstract Multiple Specialization and its Application to
Program Parallelization.
J. of Logic Programming. Special Issue on Synthesis,
Transformation and Analysis of Logic Programs, 41(2&3):279316, November
1999.
 17

G. Puebla and M. Hermenegildo.
Some Issues in Analysis and Specialization of Modular
CiaoProlog Programs.
In Special Issue on Optimization and Implementation of
Declarative Programming Languages, volume 30 of Electronic Notes in
Theoretical Computer Science. Elsevier  North Holland, March 2000.
 18

G. Puebla and M. Hermenegildo.
Abstract Specialization and its Applications.
In ACM Partial Evaluation and Semantics based Program
Manipulation (PEPM'03), pages 2943. ACM Press, June 2003.
Invited talk.
 19

G. Puebla, M. Hermenegildo, and J. Gallagher.
An Integration of Partial Evaluation in a Generic
Abstract Interpretation Framework.
In O Danvy, editor, ACM SIGPLAN Workshop on Partial Evaluation
and SemanticsBased Program Manipulation (PEPM'99), number NS991 in BRISC
Series, pages 7585. University of Aarhus, Denmark, January 1999.
 Downloading ciao, ciaopp,
lpdoc, and other CLIP software:
Last modification: Thu Nov 23 00:08:03 CET 2006 <webmaster@clip.dia.fi.upm.es>