Efficient revocation / updates in Datalog? - datalog

I am using Datalog without negation. Rules are only made of conjuctions and facts can never be removed from the system, only added.
This means that to "delete" a fact, I must create a "deletion" fact. This is sometimes called a tombstone.
For example, suppose I have a program like this:
fruit(apple).
fruit(banana).
price(apple, 0.50).
price(banana, 0.70).
If I want to find the prices of all fruits, I might do:
?- price(X, Y), fruit(X).
X = apple, Y = 0.50
X = banana, Y = 0.70
However, I want to be able to update prices, so I change my model:
fruit(apple).
fruit(banana).
price(a1, apple, 0.50).
price(b1, banana, 0.70).
price(a2, apple, 0.60).
revoke(a1).
Now my query looks like this:
?- price(P, X, Y), fruit(X).
P = a1, X = apple, Y = 0.50
P = a2, X = apple, Y = 0.60
P = b1, X = banana, Y = 0.70
There are now two prices for an apple! How do I know which prices have been revoked?
This is another query:
?- price(P, X, Y), revoke(P).
P = a1, X = apple, Y = 0.50
So now I can figure it out client-side:
P = a2, X = apple, Y = 0.60
P = b1, X = banana, Y = 0.70
However, I am wondering if it's possible to do this in a single query, but in a Datalog without negation?

Related

How can I assign variable X to only one thing in Prolog

I am writing a prolog code which has rooms and courses. A room can have multiple things such as a projector and smartboard and some of the courses needs these items.
%knowledge base
needs(101,projector).
needs(102,smart_board).
needs(241,smart_board).
needs(241,projector).
has(z23,projector).
has(z23,smart_board).
has(z06,projector).
has(z11,smart_board).
capacity(z06,50).
capacity(z11,70).
capacity(z23,90).
capacity(101,80).
capacity(102,70).
capacity(241,60).
capacity(341,40).
capacity(343,50).
%rules
assignRoom(C,R):- % C is a course, R is a Room
%course C can bi assigned to room R if:
((has(R,X),needs(C,X));%it needs 1 item and class has it or
(has(R,X),needs(C,X),has(R,Y),needs(C,Y)),X\=Y,%it needs 2 distinct items and class have both.
capacity(C, A), capacity(R, B), A=<B). %and the room can hold the number of students assigned to this course.
This code works for courses which needs one item only. But it doesn't work for courses which needs multiple items such as 241 because it holds for both conditions. How can I make the first condition checked if there is only one needs relation of the course.
Query for 102(correct):
?- assignRoom(102,X).
X = z23 ;
X = z11 ;
Query for 241(should be only z23):
?- assignRoom(241,X).
X = z23 ;
X = z23 ;
X = z11 ;
X = z23 ;
X = z23 ;
You could use forall/2 predicate.
forall(:Cond, :Action) For all alternative bindings of Cond, Action can be proven.
You can deal with all requirements in one statement.
assignRoom(C,R):-
capacity(C, A), capacity(R, B), A=<B,
forall(needs(C, X), has(R, X)).
?- assignRoom(241, X).
X = z23 ;
false.
?- assignRoom(102, X).
X = z11 ;
X = z23 ;
false.

Prolog: 'cut' in query vs. rules/facts

Doing exercise 10.4 on learnprolognow, could someone explain to me or help me visualize why for ?- p(X),p(Y) we get:
X=1,Y=1; X=1,Y=2; X=2, Y=1; X=2, Y=1.
And not just
X=1, Y=1; X=1, Y=2.
I think I'm misunderstanding how the cut happens, when it's in the ruleset instead of the query - because I think I can visualize it for ?- p(X),!,p(Y)., where it actually behaves as I thought the last one would...
Edit: From the website
% database
p(1).
p(2):-!.
p(3).
% Queries
p(X). % returns: X=1; X=2.
p(X),p(Y). % returns: X=1,Y=1; X=1, Y=1; X=2, Y=2. (?)
p(X),!,p(Y). % returns X=1, Y=1; X=1, Y=2.
To understand this problem you can imagine a tree with X in as first level and Y as second level (prolog uses sld resolution that can be well described with a tree). Consider this problem:
p(1).
p(2):-!.
p(3).
sol(X,Y):-
p(X),
p(Y).
I've added the predicate solve/2 to make it more clear. Run the query:
?- solve(X,Y).
First of all you have to choose the value for X. Prolog uses depth first search from the top to the bottom, from left to right. So it evaluates p(x): p(1) succeed (because is the first clause, if you write p(2) above p(1), p(2) will succeed) and so X = 1. Then evaluates p(Y): p(1) succeed and so you have the first solution:
X = Y, Y = 1.
If you click on more, then prolog does a backtrack (you can imagine this as a step above on the tree) and tries another value for p(Y). In this case p(2) succeed, the predicate is true and you get:
X = 1, Y = 2.
Now, if you click on more, due to the fact there is a cut (!) in the body of p(2) (a general rule in prolog has the form head :- body), prolog will not go more in depth and p(3) is ignored. So there's no more solution to p(Y). So there is another backtracking and this time, for p(X), p(2) succeed and X = 2 and for p(Y), p(1) succeed and you get:
X = 2, Y = 1.
If you click on more, you get:
X = Y, Y = 2.
Now, due to the fact there is a cut after p(2), there are no more solutions available for both X and Y (! cuts everything below p(2)).
If you remove the cut you get all the possible solutions:
X = Y, Y = 1
X = 1,
Y = 2
X = 1,
Y = 3
X = 2,
Y = 1
X = Y, Y = 2
X = 2,
Y = 3
X = 3,
Y = 1
X = 3,
Y = 2
X = Y, Y = 3
Keep in mind that the order of the clauses is important. If you write
p(2).
p(1):-!.
p(3).
You get
X = Y, Y = 2
X = 2,
Y = 1
X = 1,
Y = 2
X = Y, Y = 1
You can check this behaviour using the tracer. In SWI or SWISH you can write ?-
trace, solve(X,Y).
If you have a situation like this:
p(1).
p(2).
p(3).
sol(X,Y):-
p(X),
!,
p(Y).
prolog will tests all the possible values for Y and only one value for X because the cut stops the exploration of the tree (ideally you have 3 branches for X (1,2,3) and 3 for Y (1,2,3), ! cuts 2 and 3 from X) and you get:
X = Y, Y = 1
X = 1,
Y = 2
X = 1,
Y = 3
Sorry for the long post, hope to be clear.

In Prolog, can solutions be chosen in random order?

If I have the following:
a(X) :- X = 1; X = 2; X = 3; X = 4.
I can produce solutions in deterministic order:
?- a(X).
X = 1 ;
X = 2 ;
X = 3 ;
X = 4.
Is there any method of asking the system to produce solutions in non-deterministic, random order? For example:
?- a(X).
X = 4 ;
X = 1 ;
X = 3 ;
X = 2.
I'm aware that I can find all solutions then select one at random (findall(X, a(X), Y), random_member(Z, Y).) but this is too expensive in my case.
Possibly clearer example:
p(X,Y,Z) :-
(X = a; X = b; X = c; X = d), % (D1)
(Y = a; Y = b; Y = c), % (D2)
(Z = a; Z = b; Z = c; Z = d). % (D3)
When deterministic, generating the solution X = d, Y = c, Z = d using ?- p(X,Y,Z). will always go through the 47 previous solutions (4 * 3 * 4 = 48). However, if disjunctions are selected in non-deterministic order, the system might choose X = d at D1, Y = c at D2, Z = d at D3, generating it as the first solution.
This is being used for constrained AI-generated content, so there are many more variables in the real-world use-case.
From what you say in the comments, my impression is that a more important question for your use case is:
Can solutions be created in random order?
(This is because you say that you cannot create them all, and then choose a random one.)
To create them in a different order, Boris has hinted at a good way: Simply reorder the disjunctions!
For example, in the case you show:
p(X, Y, Z) :-
(X = a; X = b; X = c; X = d), % (D1)
(Y = a; Y = b; Y = c), % (D2)
(Z = a; Z = b; Z = c; Z = d). % (D3)
You could (automatically) create such declaratively equivalent versions of this snippet by exchanging the order if the disjunctions, such as: (X = c ; X = b ; etc.), and each of these snippets may yield the solutions in a different order.
However, it may be easier to first rewrite this to the equivalent version:
p(X, Y, Z) :-
member(X, [a,b,c,d]),
member(Y, [a,b,c]),
member(Z, [a,b,c,d]).
This way, it is easier to shuffle the lists and use the randomized lists to generate solutions.
For example, you can change this to:
p(X, Y, Z) :-
random_member(X, [a,b,c,d]),
random_member(Y, [a,b,c]),
random_member(Z, [a,b,c,d]).
random_member(X, Ls0) :-
random_permutation(Ls0, Ls),
member(X, Ls).
Now, you will get answers like:
?- p(X, Y, Z).
X = d,
Y = Z, Z = b ;
X = Z, Z = d,
Y = b ;
X = d,
Y = b,
Z = c ;
etc.
Note that this way to incorporate randomness to your code is impure: There is now implicit global state in your program, and you can no longer easily reproduce results that you need when describing test cases etc. for such programs. A solution preserving logical-purity has to make this state explicit, for example by carrying the random seed as one of the arguments, so that each run is completely reproducible.
Note that reordering conjunctions and/or goals like this works only for the pure and monotonic subset of Prolog, so make sure that you use declarative features like constraints to safely exchange goals, and to increase the generality of your code!

Enumerating domains in Prolog's clpfd

I'm exploring dependent structures of constraints like this one:
assign(X,Y) :-
X in 1..5,
((X mod 2 #= 1) #=> Y in 2..3),
((X mod 2 #= 0) #=> Y #= 5).
What I'm looking for is a representation of X's and Y's domains that is as sparse as possible - in this case it would be something along the lines of X in {1,3,5} and Y in {2,3} or X in {2,4} and Y = 5.
One way of doing that would be to detect all variables on the left side of the #=>, enumerate all their values and collect and merge them together, something like ?- assign(X, Y), findall(X-D, (indomain(X),fd_dom(Y,D)), C), do stuff with C, but maybe there is a more efficient way?
I've also encountered an error trying to label([X,Y]): Arguments are not sufficiently instantiated that goes away when I add another constraint on Y's domain.
When should I expect this error to occur? I feel I have a poor understanding of clpfd's mechanisms and limitations, are there any resources I could learn from? I already know the basics of constraint programming, arc consistency etc.
To keep clpfd enumeration predicates (like indomain/1, label/1, labeling/2, etc.) from ever throwing instantiation errors, simply ensure that all variables have been assigned some finite domain before any enumeration predicates is executed.
So how about directly translating what you wrote to code?
assign(X,Y) :- X in 1\/3\/5, Y in 2..3. % X in {1,3,5} and Y in {2,3}
assign(X,Y) :- X in 2..4, Y in 5. % or X in {2,4} and Y = 5
A simple query (with SWI-Prolog):
?- assign(X,Y), labeling([],[X,Y]).
X = 1, Y = 2
; X = 1, Y = 3
; X = 3, Y = 2
; X = 3, Y = 3
; X = 5, Y = 2
; X = 5, Y = 3
; X = 2, Y = 5
; X = 3, Y = 5
; X = 4, Y = 5.

How to add domain variable to global_cardinality?

I'm trying to add a constraint global_cardinality to my program and in the manual of SICStus Prolog is written:
global_cardinality(+Xs,+Vals)
global_cardinality(+Xs,+Vals,+Options)
where Xs = [X1,...,Xd] is a list of integers or domain variables, and
Vals = [K1-V1,...,Kn-Vn] is a list of pairs where each key Ki is a
unique integer and Vi is a domain variable or an integer. True if
every element of Xs is equal to some key and for each pair Ki-Vi,
exactly Vi elements of Xs are equal to Ki.
Now I can write:
global_cardinality([A,B,C], [1-2, 2-1]).
to say that the number 1 will be used twice. The number 2 will be used just once.
But I would like to say that the number 1 will be used: once, twice or three times
According to the manual I need a domain variable but what is the proper syntax for that?
?- X in 1..3, global_cardinality([A,B,C], [1-X, 2-1]).
not sure about this, but from SWI-Prolog page I think you could try
...global_cardinality([A,B,C], [1-X, 2-1]), (X #= 1 #\/ X #= 2 #\/ X #= 2)...
or
?- global_cardinality([A,B,C], [1-X, 2-1]), X in 1..3, label([A,B,C]).
A = B, B = 1,
C = X, X = 2 ;
A = C, C = 1,
B = X, X = 2 ;
A = X, X = 2,
B = C, C = 1.

Resources