Integrity constraints&
constraint logic programming
Henning Christiansen
Roskilde University, Denmark
INAP’99, Invited DDLP’99 talk
Constraint logic programming (CLP)
1980'ies: Built-in constraint solvers, e.g., CLP(R)
1990'ies: Constraint solving as programming paradigm,
language support, e.g., Constraint Handling Rules (CHR)
2000'ies: Efficient (WAM-like) impl's of CHR and
similar programming languages
CLP expands to more and more applications
This talk: Applying CLP for evaluation of integrity constraints
Motivation
Names of phenomena overlap:
Integrity constraint/constraint logic programming
Motivation
Names of phenomena overlap:
Integrity constraint/constraint logic programming
IC's: Knowledge about database for improved query-answering:
– controlling update request
– intentional answers
– semantic query optimization
– usually avioded, approximated by update routines/simple checks
Motivation
Names of phenomena overlap:
Integrity constraint/constraint logic programming
IC's: Knowledge about database for improved query-answering:
– controlling update request
– intentional answers
– semantic query optimization
– usually avioded, approximated by update routines/simple checks
CLP: A declarative, computational paradigm
– delay’s
– incrementality: self-simplifying, self-specializing code
– produces general (quantified) answers
Relevant for ICs!
The idea: Lazy negation-as-failure
Restrict to positive DDB's (with ”=” and ”dif”), and IC's as ”denials”:bottom :- father(A,_), mother(A,_)
bottom:- father(A,C), father(B,C), dif(A,B)
tested by negation-as-failure that bottom is not the case.
The idea: Lazy negation-as-failure
Restrict to positive DDB's (with ”=” and ”dif”), and IC's as ”denials”:bottom :- father(A,_), mother(A,_)
bottom:- father(A,C), father(B,C), dif(A,B)
tested by negation-as-failure that bottom is not the case.
Lazyness??
• A prospective update => a (constrained) ”hole” in the database
• Procedure delays when ”hole” is met
• Resulting computation:
– known part of DB checked, specialized constr’s remain for ”holes”,
– wakes up when update arrives, checking it
– ... and mutating into new specialized constraints for future updates.
The idea: Lazy negation-as-failure
Restrict to positive DDB's (with ”=” and ”dif”), and IC's as ”denials”:bottom :- father(A,_), mother(A,_)
bottom:- father(A,C), father(B,C), dif(A,B)
tested by negation-as-failure that bottom is not the case.
Lazyness??
• A prospective update => a (constrained) ”hole” in the database
• Procedure delays when ”hole” is met
• Resulting computation:
– known part of DB checked, specialized constr’s remain for ”holes”,
– wakes up when update arrives, checking it
– ... and mutating into new specialized constraints for future updates.
I.e. Incrementality!
A meta-logic approach to lazy negation-as-failure
Ground representation of object language, e.g.:
DB0 = [(bottom:- dif('A','B'), father('A','C'), father('B','C')),
(bottom:- dif('A','B'), mother('A','C'), mother('B','C')),
(bottom:- father('A','Z'), mother('A','X')),
(father(john,mary):- true),
(mother(jane,mary):- true) ]
A meta-logic approach to lazy negation-as-failure
Ground representation of object language, e.g.:
DB0 = [(bottom:- dif('A','B'), father('A','C'), father('B','C')),
(bottom:- dif('A','B'), mother('A','C'), mother('B','C')),
(bottom:- father('A','Z'), mother('A','X')),
(father(john,mary):- true),
(mother(jane,mary):- true) ]
Instance constraints:
instance(s,t) iff s, t name object S, T with T an instance of S
Example: instance(p('X'), p(a))
Instance constraints reflect object var’s to Prolog
Example:
instance(p('X'), Z) <==> Z = p(ZX)
Object unification = 2*instance + 1 Prolog unification
Instance constraints reflect object var’s to Prolog
Example:
instance(p('X'), Z) <==> Z = p(ZX)
Object unification = 2*instance + 1 Prolog unification
Example:
Object level: p(a,X) = p(Y,b)
Meta-level (= Prolog):
instance(p(a,'X'), Z1), instance(p('Y',b), Z2), Z1=Z2
Z1 = p(a, ZX), Z2 = p(ZY,b), Z1=Z2
ZX=b, XY=a
Thus: Semantic prop’s of ground repr. with efficiency of nonground repr.
Solving instance constraints... (sketch)
Straight-forward recursive decomposition, delay’s when first argument is a variable
Interacts with user constraint
constant(X) iff ”X names an object constant”
Examples:
instance(p('X'), Z) <==> Z = p(ZX)
instance(p(X), Z) <==> Z = p(ZX), instance(X,ZX)
instance(p(X),Z), constant(X) <==> constant(X), Z = p(X)
I.e. meta-var’s covered by constant take ”active” part in computation
Lazy negation-as-failure (top-level procedure)
Declaratively:
fails(p, q) iff p, q names of P, Q where Q fails in P,
i.e. no with P |– Q
Top-level of implementation:fails(P,Q):- instance(Q,Q1), fails1(P,Q1).
where fails1(p,q1) iff p, q names of P, Q1 where not P |– Q1
fails1/2 implemented by constraint solver and auxiliary constraints.
Lazy negation-as-failure (top-level procedure)
Declaratively:
fails(p, q) iff p, q names of P, Q where Q fails in P,
i.e. no with P |– Q
Top-level of implementation:fails(P,Q):- instance(Q,Q1), fails1(P,Q1).
where fails1(p,q1) iff p, q names of P, Q1 where not P |– Q1
fails1/2 implemented by constraint solver and auxiliary constraints.
... but let’s see some examples first
IC’s evaluated by lazy negation-as-failure (examples)
DB0 = [(bottom:- dif('A','B'), father('A','C'), father('B','C')),
(bottom:- dif('A','B'), mother('A','C'), mother('B','C')),
(bottom:- father('A','Z'), mother('A','X')),
(father(john,mary):- true),
(mother(jane,mary):- true) ]
IC’s evaluated by lazy negation-as-failure (examples)
DB0 = [(bottom:- dif('A','B'), father('A','C'), father('B','C')),
(bottom:- dif('A','B'), mother('A','C'), mother('B','C')),
(bottom:- father('A','Z'), mother('A','X')),
(father(john,mary):- true),
(mother(jane,mary):- true) ]
Q: fails(DB0, bottom)
IC’s evaluated by lazy negation-as-failure (examples)
DB0 = [(bottom:- dif('A','B'), father('A','C'), father('B','C')),
(bottom:- dif('A','B'), mother('A','C'), mother('B','C')),
(bottom:- father('A','Z'), mother('A','X')),
(father(john,mary):- true),
(mother(jane,mary):- true) ]
Q: fails(DB0, bottom)
A: yes
IC’s evaluated by lazy negation-as-failure (examples)
DB0 = [(bottom:- dif('A','B'), father('A','C'), father('B','C')),
(bottom:- dif('A','B'), mother('A','C'), mother('B','C')),
(bottom:- father('A','Z'), mother('A','X')),
(father(john,mary):- true),
(mother(jane,mary):- true) ]
Q: fails(DB0 & [Clause], bottom)
IC’s evaluated by lazy negation-as-failure (examples)
DB0 = [(bottom:- dif('A','B'), father('A','C'), father('B','C')),
(bottom:- dif('A','B'), mother('A','C'), mother('B','C')),
(bottom:- father('A','Z'), mother('A','X')),
(father(john,mary):- true),
(mother(jane,mary):- true) ]
Q: fails(DB0 & [Clause], bottom)
A: ... a complicated expression ready for all possibilities:
Clause could be a father clause,
a mother clause,
or a new IC, i.e, bottom:- ...
IC’s evaluated by lazy negation-as-failure (examples)
DB0 = [(bottom:- dif('A','B'), father('A','C'), father('B','C')),
(bottom:- dif('A','B'), mother('A','C'), mother('B','C')),
(bottom:- father('A','Z'), mother('A','X')),
(father(john,mary):- true),
(mother(jane,mary):- true) ]
Q: constant(A), constant(B),
fails(DB0 & [(father(A,B):- true)],bottom)
IC’s evaluated by lazy negation-as-failure (examples)
DB0 = [(bottom:- dif('A','B'), father('A','C'), father('B','C')),
(bottom:- dif('A','B'), mother('A','C'), mother('B','C')),
(bottom:- father('A','Z'), mother('A','X')),
(father(john,mary):- true),
(mother(jane,mary):- true) ]
Q: constant(A), constant(B),
fails(DB0 & [(father(A,B):- true)],bottom)
A: fails1(jane=A),fails1((mary=B, dif(john,A)))
IC’s evaluated by lazy negation-as-failure (examples)
DB0 = [(bottom:- dif('A','B'), father('A','C'), father('B','C')),
(bottom:- dif('A','B'), mother('A','C'), mother('B','C')),
(bottom:- father('A','Z'), mother('A','X')),
(father(john,mary):- true),
(mother(jane,mary):- true) ]
Q: constant(A), constant(B), no_duplicates( ... ),
fails(DB0 & [(father(A,B):- true)],bottom)
A: fails1(jane=A), fails1(mary=B)
Observations (preliminary)
• Constraint solver produces residual constraints similar to "simplification method" [Nicolas, 82; Sadri,Kowalski, 88],
but here as by-product of checking the whole, parameterized database
• A flavour of constructive negation (later example: instantiates variables)
Incrementality for sequences of updates
A new user constraint (example):
clause_pattern(F,(father(X,Y):-true),(constant(X),constant(Y)))
iff all clauses in F obey pattern and constraints
A new residual constraint:
fails1(Prog, Atom*Clause(s), Continue)
iff
for (each) instance of clause H:-B in Clause(s),
the query H=Atom, B, Continue fails in Prog.
Incrementality for sequences of updates (example)
DB0 = [(bottom:- dif('A','B'), father('A','C'), father('B','C')),
(bottom:- dif('A','B'), mother('A','C'), mother('B','C')),
(bottom:- father('A','Z'), mother('A','X')),
(father(john,mary):- true),
(mother(jane,mary):- true) ]
Q: clause_pattern(F,(father(X,Y):-true),(constant(X),constant(Y))),fails1(DB0 & F, bottom), no_duplicates(...)
Incrementality for sequences of updates (example)
DB0 = [(bottom:- dif('A','B'), father('A','C'), father('B','C')),
(bottom:- dif('A','B'), mother('A','C'), mother('B','C')),
(bottom:- father('A','Z'), mother('A','X')),
(father(john,mary):- true),
(mother(jane,mary):- true) ]
Q: clause_pattern(F,(father(X,Y):-true),(constant(X),constant(Y))),fails1(DB0 & F, bottom), no_duplicates(...)
A: fails1(DB0 & F,father(A1,Z1)*F,mother(A1,X1)),
fails1(DB0 & F,father(B2,mary)*F, dif(john,B2)),
fails1(DB0 & F,(father(A3,C3),father(B3,C3))*F, dif(A3,B3))
NB: Optimization for symmetric literals applied
View update, lazy N-a-F co-operates with abduction
A constraint-based proof predicate for program synthesis:
demo(p, q) iff p, q names of P, Q where Q succeds in P
View update, lazy N-a-F co-operates with abduction
A constraint-based proof predicate for program synthesis:
demo(p, q) iff p, q names of P, Q where Q succeds in P
DB1 = [(bottom:- ....), ...,
(sibling('A','B'):- ....), ...,
(father(john,mary):- true),
(mother(jane,mary):- true) ]
View update, lazy N-a-F co-operates with abduction
A constraint-based proof predicate for program synthesis:
demo(p, q) iff p, q names of P, Q where Q succeds in P
DB1 = [(bottom:- ....), ...,
(sibling('A','B'):- ....), ...,
(father(john,mary):- true),
(mother(jane,mary):- true) ]
Q: clause_pattern(F, (father...), ...),clause_pattern(M, (mother...), ...),
fails1(DB1 & F & M, bottom), no_duplicates(...)
demo(DB1 & F & M, sibling(bob,mary)).
View update, lazy N-a-F co-operates with abduction
A constraint-based proof predicate for program synthesis:
demo(p, q) iff p, q names of P, Q where Q succeds in P
DB1 = [(bottom:- ....), ...,
(sibling('A','B'):- ....), ...,
(father(john,mary):- true),
(mother(jane,mary):- true) ]
Q: clause_pattern(F, (father...), ...),clause_pattern(M, (mother...), ...),
fails1(DB1 & F & M, bottom), no_duplicates(...)
demo(DB1 & F & M, sibling(bob,mary)).
A: F = [(father(john,bob):-true) | F1], orM = [(mother(jane,bob):-true) | F1]
”Co-operative” human-machine dialogue (example)
U: Isn’t aunt Dora the mother of Mary? Let's ask...Q: demo(DB0, mother(dora,mary))
A: no
”Co-operative” human-machine dialogue (example)
U: Isn’t aunt Dora the mother of Mary? Let's ask...Q: demo(DB0, mother(dora,mary))
A: no
U: !?!?Hmmmm, perhaps they forgot to add it to the database?Q: fails(DB0 & [(mother(dora,mary):-true)], bottom)
”Co-operative” human-machine dialogue (example)
U: Isn’t aunt Dora the mother of Mary? Let's ask...Q: demo(DB0, mother(dora,mary))
A: no
U: !?!?Hmmmm, perhaps they forgot to add it to the database?Q: fails(DB0 & [(mother(dora,mary):-true)], bottom)
A: no
U: ... but why? I'm confused ... do you have a mother for Mary?Q: constant(A), demo(DB0, mother(A, mary))
”Co-operative” human-machine dialogue (example)
U: Isn’t aunt Dora the mother of Mary? Let's ask...Q: demo(DB0, mother(dora,mary))
A: no
U: !?!?Hmmmm, perhaps they forgot to add it to the database?Q: fails(DB0 & [(mother(dora,mary):-true)], bottom)
A: no
U: ... but why? I'm confused ... do you have a mother for Mary?Q: constant(A), demo(DB0, mother(A, mary))
A: A = jane (no more answers)
”Co-operative” human-machine dialogue (example)
U: Isn’t aunt Dora the mother of Mary? Let's ask...Q: demo(DB0, mother(dora,mary))
A: no
U: !?!?Hmmmm, perhaps they forgot to add it to the database?Q: fails(DB0 & [(mother(dora,mary):-true)], bottom)
A: no
U: ... but why? I'm confused ... do you have a mother for Mary?Q: constant(A), demo(DB0, mother(A, mary))
A: A = jane (no more answers)
U: So Jane is the only known mother, but why can't we add aunt Dora?Q: constant(A), fails(DB0 & [(mother(A,mary):- true)], bottom)
”Co-operative” human-machine dialogue (example)
U: Isn’t aunt Dora the mother of Mary? Let's ask...Q: demo(DB0, mother(dora,mary))
A: no
U: !?!?Hmmmm, perhaps they forgot to add it to the database?Q: fails(DB0 & [(mother(dora,mary):-true)], bottom)
A: no
U: ... but why? I'm confused ... do you have a mother for Mary?Q: constant(A), demo(DB0, mother(A, mary))
A: A = jane (no more answers)
U: So Jane is the only known mother, but why can't we add aunt Dora?Q: constant(A), fails(DB0 & [(mother(A,mary):- true)], bottom)
A: A = jane (no more answers)
U: ... so Jane is the only possible mother...
Observations (continued)
• Constraint solver produces residual constraints similar to "simplification method" [Nicolas, 82; Sadri, Kowalski, 88],
but here as by-product of checking the whole, parameterized database
• A flavour of constructive negation
Observations (continued)
• Constraint solver produces residual constraints similar to "simplification method" [Nicolas, 82; Sadri, Kowalski, 88],
but here as by-product of checking the whole, parameterized database
• A flavour of constructive negation
• Implies an incremental evaluation of IC’s: for each update, evaluate it, and prepare for next update
Observations (continued)
• Constraint solver produces residual constraints similar to "simplification method" [Nicolas, 82; Sadri, Kowalski, 88],
but here as by-product of checking the whole, parameterized database
• A flavour of constructive negation
• Implies an incremental evaluation of IC’s: for each update, evaluate it, and prepare for next update
• Use of constraints provides optimal interleaving with abductive procedure for free for view update (opp. algorithms [Decker, Kowalski & al, Denecker & al, ...] with explicit scheduling)
Observations (continued)
• Constraint solver produces residual constraints similar to "simplification method" [Nicolas, 82; Sadri, Kowalski, 88],
but here as by-product of checking the whole, parameterized database
• A flavour of constructive negation
• Implies an incremental evaluation of IC’s: for each update, evaluate it, and prepare for next update
• Use of constraints provides optimal interleaving with abductive procedure for free for view update (opp. algorithms [Decker, Kowalski & al, Denecker & al, ...] with explicit scheduling)
• IC’s used to produce useful answers in human-machine dialogue (though not giving ”arguments”)
Observations (continued)
• Constraint solver produces residual constraints similar to "simplification method" [Nicolas, 82; Sadri, Kowalski, 88], but here as by-product of checking the whole, parameterized database
• A flavour of constructive negation
• Implies an incremental evaluation of IC’s: for each update, evaluate it, and prepare for next update
• Use of constraints provides optimal interleaving with abductive procedure for free for view update (opp. algorithms [Decker, Kowalski & al, Denecker & al, ...] with explicit scheduling)
• IC’s used to produce useful answers in human-machine dialogue (though not giving ”arguments”)
... now, let’s have a look at that constraint solver
Constraint solver for lazy negation-as-failure– Simplified CHR’s; first version for known program and query
Constraint solver for lazy negation-as-failure– Simplified CHR’s; first version for known program and query
fails1(_,true) <=> fail
Constraint solver for lazy negation-as-failure– Simplified CHR’s; first version for known program and query
fails1(_,true) <=> fail
fails1(P, (Q1,(A=B),Q2)) <=>
(A=B -> fails1(P,(Q1,Q2));true)
Constraint solver for lazy negation-as-failure– Simplified CHR’s; first version for known program and query
fails1(_,true) <=> fail
fails1(P, (Q1,(A=B),Q2)) <=>
(A=B -> fails1(P,(Q1,Q2));true)
fails1(P, (Q1,dif(A,B),Q2)) <=>
A==B or both are constants or
one is a variable free in (Q1,Q2) | (A==B -> true;fails1(P,(Q1,Q2)))
Constraint solver for lazy negation-as-failure– Simplified CHR’s; first version for known program and query
fails1(_,true) <=> fail
fails1(P, (Q1,(A=B),Q2)) <=>
(A=B -> fails1(P,(Q1,Q2));true)
fails1(P, (Q1,dif(A,B),Q2)) <=>
A==B or both are constants or
one is a variable free in (Q1,Q2) | (A==B -> true;fails1(P,(Q1,Q2)))
fails1(P, (Q1, A, Q2)) <=>
A names an atom |
fails1(P, A*P, (Q1,Q2))
Constraint solver for lazy negation-as-failure– Simplified CHR’s; first version for known program and query
fails1(_,true) <=> fail
fails1(P, (Q1,(A=B),Q2)) <=>
(A=B -> fails1(P,(Q1,Q2));true)
fails1(P, (Q1,dif(A,B),Q2)) <=>
A==B or both are constants or
one is a variable free in (Q1,Q2) | (A==B -> true;fails1(P,(Q1,Q2)))
fails1(P, (Q1, A, Q2)) <=>
A names an atom |
fails1(P, A*P, (Q1,Q2))
fails1(_, _*[], _) <=> true
Constraint solver for lazy negation-as-failure– Simplified CHR’s; first version for known program and query
fails1(_,true) <=> fail
fails1(P, (Q1,(A=B),Q2)) <=>
(A=B -> fails1(P,(Q1,Q2));true)
fails1(P, (Q1,dif(A,B),Q2)) <=>
A==B or both are constants or
one is a variable free in (Q1,Q2) | (A==B -> true;fails1(P,(Q1,Q2)))
fails1(P, (Q1, A, Q2)) <=>
A names an atom |
fails1(P, A*P, (Q1,Q2))
fails1(_, _*[], _) <=> true
fails1(P, A*[C|Cs], Cont) <=>
fails1(P, A*C, Cont),
fails1(P, A*Cs, Cont)
Constraint solver for lazy negation-as-failure– Simplified CHR’s; first version for known program and query
fails1(_,true) <=> fail
fails1(P, (Q1,(A=B),Q2)) <=>
(A=B -> fails1(P,(Q1,Q2));true)
fails1(P, (Q1,dif(A,B),Q2)) <=>
A==B or both are constants or
one is a variable free in (Q1,Q2) | (A==B -> true;fails1(P,(Q1,Q2)))
fails1(P, (Q1, A, Q2)) <=>
A names an atom |
fails1(P, A*P, (Q1,Q2))
fails1(_, _*[], _) <=> true
fails1(P, A*[C|Cs], Cont) <=>
fails1(P, A*C, Cont),
fails1(P, A*Cs, Cont)
fails1(P,A*(H:-B), Cont) <=>
copy_term((A,Cont),(A1,Cont1)),
instance((H:-B),(H1,B1)),
fails1(P,(H1=A1,B1, Cont1))
Constraint solver for lazy negation-as-failure– Simplified CHR’s; first version for known program and query
fails1(P,A*(H:-B), Cont) <=>
copy_term((A,Cont),(A1,Cont1)),
instance((H:-B),(H1,B1)),
fails1(P,(H1=A1,B1, Cont1))
Adapt for partly known object program and query
• Definition:
External variable: appears in arg's to fails
Externally dependent variable A:
A external or instance(External,...A...)
Adapt for partly known object program and query
• Definition:
External variable: appears in arg's to fails
Externally dependent variable A:
A external or instance(External,...A...)
• Replace copy_term by internal_copy_term: as copy_term, but
• keep external variables,
• copy instance constraints (also keeping external var’s)
Adapt for partly known object program and query
• Definition:
External variable: appears in arg's to fails
Externally dependent variable A:
A external or instance(External,...A...)
• Replace copy_term by internal_copy_term: as copy_term, but
• keep external variables,
• copy instance constraints (also keeping external var’s)
• Principle:
– process literals where result (yes/no) is predictable,
– delay for the rest (typically due to external variables)
– various optimizations for = and dif
Correctness properties (still need to be formalized)
• Local soundness and completeness of each rule obvious
Correctness properties (still need to be formalized)
• Local soundness and completeness of each rule obvious
• Computation rule á la Prolog => termination á la Prolog
Correctness properties (still need to be formalized)
• Local soundness and completeness of each rule obvious
• Computation rule á la Prolog => termination á la Prolog
• More eager com. rule => better ”precision” in detecting inconsistencies early; problematic for recursive programs
Efficient implementations
Prolog-like setting:
• Replace metainterpreter by extended WAM functionality
copy_internal_term replaced by operations on WAM stacks
• CHR’s loop for rules-to-apply replaced by low-level message passing – allowing complex guards to simplify dynamically
• Needs more detailed control of unfolding in order to employ Prolog’s indexing techniques
Ex: dif(peter,mary), dif(peter,dolly), ....
=> \+ mother(peter,_)
Efficient implementations
Prolog-like setting:• Replace metainterpreter by extended WAM functionality
copy_internal_term replaced by operations on WAM stacks• CHR’s loop for rules-to-apply replaced by low-level message passing –
allowing complex guards to simplify dynamically• Needs more detailed control of unfolding in order to employ Prolog’s
indexing techniques
Ex: dif(peter,mary), dif(peter,dolly), ....
=> \+ mother(peter,_)
Relational database (RDB) setting:• Use Prolog as ”semantic engine” interfaced with RDB as efficient
fact base• Embed functionality in RBD system
Conclusion
• Relating integrity constraints and constraint logic programming:
– Integrity constraints represented as calls to CLP constraints
• Strategy: lazy negation-as-failure
• CLP provides
– incremental evaluation
– self-specializing, self-simplifying code
– not unlike a partial evaluation approach [Leuschel, De Schreye, 1998]
Conclusion
• Relating integrity constraints and constraint logic programming:– Integrity constraints represented as calls to CLP constraints
• Strategy: lazy negation-as-failure• CLP provides
– incremental evaluation– self-specializing, self-simplifying code
– not unlike a partial evaluation approach [Leuschel, De Schreye, 1998]
To do next:• consider update by deletion
– in present model must be described by deleted_facts• include negation to object language
– we expect: possible, but with the ”usual” complexity• try out suggested ”efficient implementations”
Final remark
CLP highly suited for evaluation of ICs
^
and modeling!
Top Related