|
Andrew Santosa Research Fellow Department of Computer Science National University of Singapore Computing 1 #02-15, 13 Computing Drive, Singapore 117417 Republic of Singapore Phone: +65-6516-2792, Mobile: +65-9482-7396 FAX: +65-6779-4580 (Attn: Andrew E. Santosa/RF) E-Mail:
|
I am working towards achieving the goal of industrial production of dependable software. In particular, I am currently working on verification and analysis of programs (in general, anything that can be specified as recursive function or relation, both deterministic (sequential) or nondeterministic (concurrent), even with fairness requirement) by means of symbolic execution, especially using constraint logic programming (CLP). The basic approach is to invent novel non-heuristic techniques for search algorithms. The application area currently being targeted at encompasses the analysis of C source code, verification of real-time systems, and reasoning about omega automata (for which live sequence charts are instances).
I am also tuning in to the developments in programming language technology and software engineering.
In my computer science work, I frequently deal with fixpoint computation. Now, fixpoint computation is founded upon semantics, and therefore we need to look up the dictionary, say, the Oxford English dictionary, where you can find recursive definitions, such as:
My degrees are Doctor of Philosophy (Computer Science, National University of Singapore, Singapore, 2008), Master of Engineering (Graduate School of Information Systems, University of Electro-Communications (UEC), Tokyo, Japan, 1999), Bachelor of Engineering (Computer Science and Information Mathematics, UEC, Tokyo, Japan, 1997).
Here are some of the fruits of research projects led by Professor Joxan Jaffar and with the collaboration of Dr. Razvan Voicu in which I am a member. It resulted in my PhD thesis mentioned below.
|
|
|
|
|
|
Declarative Concurrent Programming
This work is the result of my collaboration with Professor Rafael Ramirez of Universitat Pompeu Fabra.
|
Selected Other Work
(Under Construction)
When a programmer suspects that a call to a predicate, say, p(X,Y,Z) does not behave correctly, an easy way to check input-output behavior is to first identify the input variables of the predicate. If uncertain, only pick as input variables conservatively (only those you are certain to be input variables). Suppose that in the call p(X,Y,Z), we know that X and Y are input. Now, before the call, add a printf and a read, resulting in:
printf("p(%, %, Z)\n", [X, Y]),
read(_),
p(X, Y, Z),
instead of the original call.
In this way, when it is about to execute the call, the program would
first print, say, p(1, [2,3], Z) on the
terminal and waits for standard input. Here, the programmer can
input something (say x.) on the
standard input to continue to the next breakpoint. More importantly,
the programmer terminate the program (by Ctrl-C), then copy the output
p(1, [2,3], Z) to CLP(R)'s prompt, and
execute it in isolation to observe the behavior.
Software engineering wise, this allows rapid writing of CLP(R) programs to be debugged later. I need to do more programming myself to confirm that the technique is actually effective for rapid production of CLP(R) programs.
Suppose that we want to prove X > 2 => X > 0. To negate X > 0, we use the following procedure:
p(X, X1) :-
dump([X1], [Y], L), test_cnf(L, X, Y).
test_cnf(L,X,Y) :- negate_cnf(L), X=Y, !, fail.
test_cnf(L,X,Y).
negate_cnf([]).
negate_cnf([C|_]) :- negate_constraint(C).
negate_cnf([_|R]) :- negate_cnf(R).
negate_constraint(quote(A > B)) :- expreval(A), expreval(B), A <= B.
...
expreval(X) :- var(A).
expreval(X) :- atomic(A).
...
expreval(quote(A + B)) :- A + B.
...
The main point here is the use of
dump/3
to produce the quoted form of the constraint at hand, which
we can then negate.
To prove the implication X > 2 => X > 0, we run
:- X > 2, X1 > 0, p(X, X1).
p(X) :- X=Y+2, p(Y). p(0).Here we just want to know if there is a solution to the query
:- p(X).Obviously there is an answer to the query, but SLD does not terminate on this query, due to the standard top-to-bottom clause selection rule. The way we implement tabling here is closely related to the proof of implication we have given in the previous section. Here a query is tabled if there exists another query previously executed that subsumes the current query. Sumsumption here means logical implication. Some tabling mechanism uses variance instead of subsumption (see e.g. in T Swift, A New Formulation of Tabled Resolution with Delay, EPIA '99, LNCS 1695, Springer, 1999), which requires similarity up to renaming, and which is logically stronger than subsumption but perhaps easier to compute.
Let us now add a few things to our program, which now looks like:
:- dynamic(p_t,1). p(X) :- check(X), X=Y+2, p(Y). p(0). check(X) :- not p_t(X), !, fail. check(X) :- dump([X],[Y],L), negate_cnf(L), assert(p_t(Y)), fail. check(X). negate_cnf([]). negate_cnf([C|_]) :- negate_constraint(C). negate_cnf([_|R]) :- negate_cnf(R). negate_constraint(quote(A=B)) :- expreval(A), expreval(B), AAfter executing :- p(X), the check/1 will be tested to see if p(X) is already called, if not, p_t(X) is tabled and the query :- X=Y+2, p(Y) is then executed. Notice that this query is logically subsumed by ancestor query p(X). Since both impose no constraint on their arguments. When executing the child query, the CLP(R) will discover that p(X) has been called and fails the first clause, branch, which then makes the second clause p(0). be executed resulting in answer X=2.B. negate_constraint(quote(A=B. negate_constraint(quote(A>B)) :- expreval(A), expreval(B), A<=B. ...
Now a little discussion on how this tabling differs from SLG. SLG is capable of returning all answers of a query since it records all answers to a query. The tabling technique described here only reports some answer. However, if there is some answer, at least one answer will be returned. This kind of tabling is useful for implementing existential query systems, e.g. verification or analysis tools (is there a violation of safety property?).
This tabling technique can be used to construct a reachability checker, among others. The following is a reachability checker for proving mutual exclusion of Bakery algorithm that uses the technique described here.
Sample usage:
1 ?- X + 2 * Y <= 7, 2*X - Y<=3, X>=0, Y>=0, intsoln([X,Y]). X=2 Y=2 *** Retry?