Related
For the CLP(B) library of SWI-Prolog,
I want to implement a weighted version of sat_count/2
sat_count(Sat0, N) :-
catch((parse_sat(Sat0, Sat),
sat_bdd(Sat, BDD),
sat_roots(Sat, Roots),
roots_and(Roots, _-BDD, _-BDD1),
% we mark variables that occur in Sat0 as visited ...
term_variables(Sat0, Vs),
maplist(put_visited, Vs),
% ... so that they do not appear in Vs1 ...
bdd_variables(BDD1, Vs1),
partition(universal_var, Vs1, Univs, Exis),
% ... and then remove remaining variables:
foldl(universal, Univs, BDD1, BDD2),
foldl(existential, Exis, BDD2, BDD3),
variables_in_index_order(Vs, IVs),
foldl(renumber_variable, IVs, 1, VNum),
bdd_count(BDD3, VNum, Count0),
var_u(BDD3, VNum, P),
% Do not unify N directly, because we are not prepared
% for propagation here in case N is a CLP(B) variable.
N0 is 2^(P - 1)*Count0,
% reset all attributes and Aux variables
throw(count(N0))),
count(N0),
N = N0).
I did not find a detailed documentation of the library for modifying the code.
How to implement a weighted version of sat_count/2?
EDIT 1 (01/11/2017):
Thank you #mat for your reply, I can't add comments because I've not enough reputation.
weighted_sat_count/3 should take a list of couples of weights, one for each variable (a weight for True and a weight for False state) and then the other two parameters are the same of sat_count/2.
The Count is the sum of weights of each admissible assignment. The weight of each admissible assignment is the product of the weight of each variable.
The algorithm to calculate the result is:
bdd_weight(BDD_node)
if BDD_node is 1-terminal return 1
if BDD_node is 0-terminal return 0
t_child <- 1-child of BDD_node
f_child <- 0-child of BDD_node
return (weight[BDD_node, 1] * bdd_weight(t_child) + weight[BDD_node, 0] * bdd_weight(f_child))
The algorithm can be more efficient with a map of visited node associated with calculated weight.
weight[,] is the list of couples of weights, 1 for True and 0 for False.
EDIT 2 (03/11/2017):
For example:
A+B+C, a simple SAT formula
List of couple for weights: [(0.7, 0.3), (0.9, 0.1), (0.5, 0.5)], one for each varible
?- weighted_sat_count([(0.7, 0.3), (0.9, 0.1), (0.5, 0.5)], +([A, B, C]), Count).
Count =
0.7*0.9*0.5 +
0.3*0.9*0.5 +
0.7*0.1*0.5 +
...
A non-efficient solution, based on modifying another part of a simple sat solver, starts with looking at a more simpler count code:
% my_sat_count(+List, -Integer)
my_sat_count([X|L], C) :-
findall(D, (X=0, my_sat_count(L,D);
X=1, my_sat_count(L,D)), H),
sum_list(H, C).
my_sat_count([], 1).
% sum_list(+List, -Number)
sum_list([D|L], C) :-
sum_list(L, H),
C is D+H.
sum_list([], 0).
To see that this simple code works, lets make an example (can be run in both SWI-Prolog or Jekejeke Prolog with the Minlog Extension):
Jekejeke Prolog 2, Runtime Library 1.2.5
(c) 1985-2017, XLOG Technologies GmbH, Switzerland
?- use_module(library(finite/clpb)).
% 8 consults and 0 unloads in 93 ms.
Yes
?- sat(X#Y#Z), labeling([X,Y,Z]).
X = 0, Y = 0, Z = 1 ;
X = 0, Y = 1, Z = 0 ;
X = 1, Y = 0, Z = 0 ;
X = 1, Y = 1, Z = 1
?- sat(X#Y#Z), my_sat_count([X,Y,Z],N).
N = 4,
Now adding weighting is a simple extension as follows:
% my_weighted_sat_count(+List, +Pairs, -Float)
my_weighted_sat_count([X|L], [(P,Q)|R], C) :-
findall(D, (X=0, my_weighted_sat_count(L,R,J), D is P*J;
X=1, my_weighted_sat_count(L,R,J), D is Q*J), H),
sum_list(H, C).
my_weighted_sat_count([], _, 1.0).
Here are some example runs:
?- sat(X#Y#Z), my_weighted_sat_count([X,Y,Z],
[(0.5,0.5),(0.4,0.6),(0.3,0.7)],W).
W = 0.5
?- sat(X#Y#Z), my_weighted_sat_count([X,Y,Z],
[(0.3,0.7),(0.3,0.7),(0.3,0.7)],W).
W = 0.532
test(X, Y) :-
X ins 1..3,
Y ins 1..3,
X #\= Y.
Here is my attempt at doing it. The goal would be to type this into SWI-Prolog so that this output comes out.
?- test(X, Y).
X = 1
Y = 2 ;
X = 2,
Y = 1;
X = 3,
Y = 1 ;
... etc.
I'm actually trying to solve the 8-queens problem using prolog and have this so far.
eight_queens(Qs, L) :-
Qs = [ [X1,Y1], [X2, Y2], [X3, Y3], [X4, Y4], [X5, Y5], [X6, Y6], [X7, Y7], [X8, Y8], [X9, Y9] ],
Qs ins 1..9,
X1 #\= X2,
X1 #\= X3,
...
etc.
But I keep getting this error: "Arguments are not sufficiently instantiated" for both the test function and the eight_queens problem.
Besides the observation about in/2 and ins/2 posted by #coder, that solve your imminent problem, I would add the following points that are good to keep in mind when using CLP(FD):
1. Always make labeling the last goal
First let's observe the answers for the variant marked as 2nd way using ins in #coder's post but without the goal label/1:
test(X, Y) :-
[X,Y] ins 1..3,
X #\= Y.
?- test(X,Y).
X in 1..3, % residual goal
X#\=Y, % residual goal
Y in 1..3. % residual goal
Since there is no unique answer to the query, Prolog answers with residual goals (see section A.8.8 of the CLP(FD) manual) for more information). These residual goals are constraints that are being propagated and with every additional (non-redundant) constraint the domain is narrowed. If this does not lead to a unique solution like in the example above you can get concrete values by labeling the constrained variables (e.g. with label/1). This observation suggests to use labeling as the last goal:
?- test(X,Y), label([X,Y]).
X = 1,
Y = 2 ;
X = 1,
Y = 3 ;
X = 2,
Y = 1 ;
X = 2,
Y = 3 ;
X = 3,
Y = 1 ;
X = 3,
Y = 2.
This is obviously the same result as with #coders version but the three pairs (X,Y) = (1,1) ∨ (2,2) ∨ (3,3) are not considered when labeling due to the constraint X#\=Y being posted before the goal label([X,Y]). In #coder's version it is the other way around: label([X,Y]) is delivering all three pairs as possible solutions and the last goal X#\=Y is eliminating them subsequently. To see this just leave the last goal as a comment and query the predicate:
test(X,Y):- [X,Y] ins 1..3, label([X,Y]). %, X#\=Y.
?- test(X,Y).
X = Y, Y = 1 ; % <- (1,1)
X = 1,
Y = 2 ;
X = 1,
Y = 3 ;
X = 2,
Y = 1 ;
X = Y, Y = 2 ; % <- (2,2)
X = 2,
Y = 3 ;
X = 3,
Y = 1 ;
X = 3,
Y = 2 ;
X = Y, Y = 3. % <- (3,3)
The difference is minuscule in this example, so there's nothing wrong with #coder's version. But in general this might lead to a big difference if the constraints posted after labeling exclude a lot of candidates. So it's good practice to always put labeling as the last goal.
2. Separate labeling from the actual relation
Coming from the previous observations it is opportune to divide the predicate into a core relation that is posting all the constraints and labeling. Consider the restructured predicate test/2 as a template:
test(X,Y) :-
test_(X,Y,L), % the core relation
label(L). % labeling
test_(X,Y,L) :-
L=[X,Y], % variables to be labeled in a flat list
L ins 1..3,
X#\=Y.
The predicate test_/3 is describing the actual relation by posting all the necessary constraints and has a list as an additional argument that contains all the variables to be labeled. Obtaining the latter might not be trivial, depending on the data structures your arguments come with (consider for example a list of lists as an argument that you want to turn into a flat list for labeling). So the predicate test/2 is only calling test_/3 and subsequently the labeling goal. This way you have a clean and easily readable separation.
3. Try different labeling strategies
The goal label(L) is the simplest way to do labeling. It is equivalent to labeling([],L). The first argument of labeling/2 is a list of options that gives you some control over the search process, e.g. labeling([ff],L) labels the leftmost variable with the smallest domain next, in order to detect infeasibility early. Depending on the problem you are trying to solve different labeling strategies can lead to results faster or slower. See the documentation of labeling/2 for available labeling strategies and further examples.
ins is used for lists, in is used for single variable so in your example:
test(X, Y) :-
X ins 1..3,
Y ins 1..3,
X #\= Y.
X,Y are assumed to be lists. This does not produces a syntax error, but produces error when trying to run it with X,Y not being lists.
Also when using in Low..High doesn't mean that the variable is int just X=<High and X>=Low. In order to put the constraint to be integers use label/1:
:- use_module(library(clpfd)).
%using in/
test(X,Y):- X in 1..3,Y in 1..3,label([X,Y]), X#\=Y.
%2nd way using ins
test(X,Y):- [X,Y] ins 1..3, label([X,Y]), X#\=Y.
Example:
?- test(X,Y).
X = 1,
Y = 2 ;
X = 1,
Y = 3 ;
X = 2,
Y = 1 ;
X = 2,
Y = 3 ;
X = 3,
Y = 1 ;
X = 3,
Y = 2 ;
false.
I'm trying to get Aleph to work and induce a simple theory: grandparent(X,Z):- father(X, Y), father(Y, Z). However i get back an atom (e.g. grandparent(john,johnJuniorJunior)). Hope somebody can help. Note that Aleph for SWI uses a single file as input.
Cheers/JC
My program:
:- use_module(library(aleph)).
:- aleph.
:- modeh(*,grandparent(+person,-person)).
:- modeb(*,father(+person,-person)).
:-begin_bg.
person(john).
person(johnJunior).
person(johnJuniorJunior).
person(jack).
person(jackJunior).
person(jackJuniorJunior).
father(johnJunior, john).
father(johnJuniorJunior, johnJunior).
father(jackJunior, jack).
father(jackJuniorJunior, jackJunior).
:-determination(grandparent/2,father/2).
:-end_bg.
:-begin_in_pos.
grandparent(john, johnJuniorJunior).
grandparent(jack, jackJuniorJunior).
:-end_in_pos.
:-begin_in_neg.
grandparent(jack, john).
:-end_in_neg.
:-aleph_read_all.
My output:
[theory]
[Rule 1] [Pos cover = 1 Neg cover = 0]
grandparent(john,johnJuniorJunior).
[Rule 2] [Pos cover = 1 Neg cover = 0]
grandparent(jack,jackJuniorJunior).
[time taken] [0.0]
[total clauses constructed] [2]
true.
Changing
:- modeh(*,grandparent(+person,-person)).
:- modeb(*,father(+person,-person)).
To
:- modeh(*,grandparent(+person,+person)).
:- modeb(*,father(-person,-person)).
Solved my issue. Thanks Fabrizio!
I'm trying to generate every combination possible given a list. I want every [X,Y] combination possible.
Here's an example :
?- arguments(A,[1,2,3]).
A = [1,2] ; A = [1,3] ; A = [2,1] ; A = [2,3] ; A = [3,1] ;
A = [3,2]
I have tried multiple things, but I've yet to find a working one.
I am pretty sure the idea is to use prolog's ability to try every possibility as such :
element(X,[X|_],1).
element(X,[_|Q],N) :- element(X,Q,NewN), N is NewN + 1.
This predicate can return the element at the position N, or return the position of the element X, or generate every possibility. Exemple :
?- element(X,[a,b,c],N).
N = 1
X = a
N = 2
X = b
N = 3
X = c
Thanks for the help.
Edit following gusbro answer :
I can't use already existing predicates, it's part of a course.
Reading your answer, I came up with this :
remove_element_x(X, [X|Q], Q).
remove_element_x(X, [T|Q], [T|Res]) :- remove_element_x(X,Q,Res).
arguments([X,Y],L) :-
element(X,L,_),
remove_element_x(X,L,L2),
element(Y,L2,_).
remove_element_x/3 remove the element x from the list and returns the new list.
But the backtracking is not working :
?- arguments(A,[1,2,3]).
A = [1,2] ?
yes
You can use select/3 to select an element from a list (and get the remaining list), then do it again to select another element from the remaining list).
i.e.:
arguments([A,B], L):-
select(A, L, L1),
select(B, L1,_).
Test case:
?- arguments(A,[1,2,3]).
A = [1, 2] ;
A = [1, 3] ;
A = [2, 1] ;
A = [2, 3] ;
A = [3, 1] ;
A = [3, 2]
i am trying to use WARMR to find frequent relational patterns in my data; for this i am using ALEPH in SWI-Prolog. however, i am struggling to figure out how to do this and why my previous attempts did not work.
i want to make a toy example work before i move on to my full data. for this i took the toy "train" data from the aleph pack page:
http://www.swi-prolog.org/pack/list?p=aleph
the Aleph manual states about the ar search:
ar Implements a simplified form of the type of association rule search conducted by the WARMR system (see L. Dehaspe, 1998, PhD Thesis, Katholieke Universitaet Leuven). Here, Aleph simply finds all rules that cover at least a
pre-specified fraction of the positive examples. This fraction is specified by the parameter pos_fraction.
accordingly i have inserted
:- set(search,ar).
:- set(pos_fraction,0.01).
into the background file (and deleted :- set(i,2).)) and erased the .n file of negative examples. i have also commented out all the determinations and the modeh declaration logic being that we are searching for frequent patterns, not rules (i.e. in a supervised context head would be an "output" variable and clauses in the body -- "inputs" trying to explain the output), i.e. it is an unsupervised task.
now, the original trains dataset is trying to construct rules for "eastbound" trains. this is done by having predicates like car, shape, has_car(train, car) etc. originally all the background knowledge relating to these is located in the .b file and the five positive examples (e.g eastbound(east1).) in the .f file (+ five negative examples, e.g. eastbound(west1)., in the .n file). leaving files unchanged (save for the changes described above) and running induce. does not produce a sensible result (it would return ground terms like train(east1) as a "rule", for example). i have tried moving some of the background knowledge to the .f file but that did not produce anything sensible either.
how do i go about constructing the .f and .b files? what should to into the positive examples file if we are not really looking to explain any positive examples (which would surely constitute a supervised problem) but instead to find frequent patterns in the data (unsupervised problem)? am i missing something?
any help would be greatly appreciated.
First of all if you can use the original WARMR I think it is better. But I think you need to be an academic for free use. You can try asking for a license.
https://dtai.cs.kuleuven.be/ACE/
To get association rules, I put all the examples I want in the f file. The n file can have examples in it or I think be empty.
The only thing I change is to put :
:- set(search,ar).
:- set(pos_fraction,0.01).
In the .b file. Keep the determinations and mode declarations.
The set(i,2) limits the length of the query to having two additional literals (I think) so you might want this to be larger.
?-read_all(train).
induce.
You will then get an out of 'good clauses' which I think are the frequent queries.
[good clauses]
eastbound(A).
[pos cover = 5 neg cover = 0] [pos-neg] [5]
eastbound(A) :-
has_car(A,B).
[pos cover = 5 neg cover = 0] [pos-neg] [5]
eastbound(A) :-
has_car(A,B).
[pos cover = 5 neg cover = 0] [pos-neg] [5]
eastbound(A) :-
has_car(A,B).
[pos cover = 5 neg cover = 0] [pos-neg] [5]
eastbound(A) :-
has_car(A,B).
[pos cover = 5 neg cover = 0] [pos-neg] [5]
eastbound(A) :-
has_car(A,B), long(B).
[pos cover = 2 neg cover = 0] [pos-neg] [2]
eastbound(A) :-
has_car(A,B), open_car(B).
[pos cover = 5 neg cover = 0] [pos-neg] [5]
eastbound(A) :-
has_car(A,B), shape(B,rectangle).
[pos cover = 5 neg cover = 0] [pos-neg] [5]
eastbound(A) :-
has_car(A,B), wheels(B,2).
[pos cover = 5 neg cover = 0] [pos-neg] [5]
eastbound(A) :-
has_car(A,B), load(B,rectangle,3).
[pos cover = 1 neg cover = 0] [pos-neg] [1]
eastbound(A) :-
has_car(A,B), has_car(A,C).
[pos cover = 5 neg cover = 0] [pos-neg] [5]
eastbound(A) :-
has_car(A,B), has_car(A,C).
[pos cover = 5 neg cover = 0] [pos-neg] [5]
eastbound(A) :-
has_car(A,B), has_car(A,C).
[pos cover = 5 neg cover = 0] [pos-neg] [5]
eastbound(A) :-
has_car(A,B), short(B).
[pos cover = 5 neg cover = 0] [pos-neg] [5]
eastbound(A) :-
has_car(A,B), closed(B).
[pos cover = 5 neg cover = 0] [pos-neg] [5]
eastbound(A) :-
has_car(A,B), shape(B,rectangle).
[pos cover = 5 neg cover = 0] [pos-neg] [5]
eastbound(A) :-
has_car(A,B), wheels(B,2).
[pos cover = 5 neg cover = 0] [pos-neg] [5]
eastbound(A) :-
has_car(A,B), load(B,triangle,1).
[pos cover = 5 neg cover = 0] [pos-neg] [5]
eastbound(A) :-
has_car(A,B), has_car(A,C).
[pos cover = 5 neg cover = 0] [pos-neg] [5]
eastbound(A) :-
has_car(A,B), has_car(A,C).
[pos cover = 5 neg cover = 0] [pos-neg] [5]
eastbound(A) :-
has_car(A,B), long(B).
[pos cover = 2 neg cover = 0] [pos-neg] [2]
eastbound(A) :-
has_car(A,B), open_car(B).
[pos cover = 5 neg cover = 0] [pos-neg] [5]
eastbound(A) :-
has_car(A,B), shape(B,rectangle).
[pos cover = 5 neg cover = 0] [pos-neg] [5]
eastbound(A) :-
has_car(A,B), wheels(B,3).
[pos cover = 3 neg cover = 0] [pos-neg] [3]
eastbound(A) :-
has_car(A,B), load(B,hexagon,1).
[pos cover = 1 neg cover = 0] [pos-neg] [1]
eastbound(A) :-
has_car(A,B), has_car(A,C).
[pos cover = 5 neg cover = 0] [pos-neg] [5]
eastbound(A) :-
has_car(A,B), short(B).
[pos cover = 5 neg cover = 0] [pos-neg] [5]
eastbound(A) :-
has_car(A,B), open_car(B).
[pos cover = 5 neg cover = 0] [pos-neg] [5]
eastbound(A) :-
has_car(A,B), shape(B,rectangle).
[pos cover = 5 neg cover = 0] [pos-neg] [5]
eastbound(A) :-
has_car(A,B), wheels(B,2).
[pos cover = 5 neg cover = 0] [pos-neg] [5]
eastbound(A) :-
has_car(A,B), load(B,circle,1).
[pos cover = 3 neg cover = 0] [pos-neg] [3]
eastbound(A) :-
has_car(A,B), open_car(B), shape(B,rectangle).
[pos cover = 4 neg cover = 0] [pos-neg] [4]
etc etc
The rules are of the form eastbound(A):-blah blah. But it is only counting the eastbound examples. So think of this as example_covered(A):-blah blah