Generate and Test accumulating valid answer for next test - prolog

I know how to do a simple generate and test to return each answer individually. In the following example only items that are greater than 1 are returned.
item(1).
item(1).
item(2).
item(3).
item(1).
item(7).
item(1).
item(4).
gen_test(Item) :-
item(Item), % generate
Item > 1. % test
?- gen_test(Item).
Item = 2 ;
Item = 3 ;
Item = 7 ;
Item = 4.
I can also return the results as a list using bagof/3
gen_test_bagof(List) :-
bagof(Item,(item(Item),Item > 1), List).
?- gen_test_bagof(List).
List = [2, 3, 7, 4].
Now I would like to be able to change the test so that it uses member/2 with a list where the list is the accumulation of all previous valid answers.
I have tried this
gen_test_acc_facts(L) :-
gen_test_acc_facts([],L).
gen_test_acc_facts(Acc0,R) :-
item(H), % generate
(
member(H,Acc0) % test
->
gen_test_acc_facts(Acc0,R) % Passes test, don't accumulate, run generate and test again.
;
gen_test_acc_facts([H|Acc0],R) % Fails test, accumulate, run generate and test again.
).
but since it is recursive, every call of item/1 results in the same first fact being used.
I suspect the answer will require eliminating backtracking as was done in this answer by mat because this needs to preserve information over backtracking.
Details
The example given is a simplified version of the real problem which is to generate graphs with N vertices where the edges are undirected, have no
loops (self references), have no weights and the vertexes are labeled and there is no root vertex and set of graphs is only the isomorphic graphs. Generating all of the graphs for N is easy, and while I can accumulate all of the graphs into a list first, then test all of them against each other; once N=8 the memory is exhausted.
?- graph_sizes(N,Count).
N = 0, Count = 1 ;
N = Count, Count = 1 ;
N = Count, Count = 2 ;
N = 3, Count = 8 ;
N = 4, Count = 64 ;
N = 5, Count = 1024 ;
N = 6, Count = 32768 ;
N = 7, Count = 2097152 ;
ERROR: Out of global stack
However as there are many redundant isomorphic graphs generated, by pruning the list as it grows, the size of N can be increased. See: Enumerate all non-isomorphic graphs of a certain size
gen_vertices(N,L) :-
findall(X,between(1,N,X),L).
gen_edges(Vertices, Edges) :-
findall((X,Y), (member(X, Vertices), member(Y, Vertices), X < Y), Edges).
gen_combination_numerator(N,Numerator) :-
findall(X,between(0,N,X),L0),
member(Numerator,L0).
comb(0,_,[]).
comb(N,[X|T],[X|Comb]) :-
N>0,
N1 is N-1,
comb(N1,T,Comb).
comb(N,[_|T],Comb) :-
N>0,
comb(N,T,Comb).
gen_graphs(N,Graph) :-
gen_vertices(N,Vertices),
gen_edges(Vertices,Edges),
length(Edges,Denominator),
gen_combination_numerator(Denominator,Numerator),
comb(Numerator,Edges,Graph).
graph_sizes(N,Count) :-
length(_,N),
findall(.,gen_graphs(N,_), Sols),
length(Sols,Count).
The test for isomorphic graphs in Prolog.
Examples of generated graphs:
?- gen_graphs(1,G).
G = [] ;
false.
?- gen_graphs(2,G).
G = [] ;
G = [(1, 2)] ;
false.
?- gen_graphs(3,G).
G = [] ;
G = [(1, 2)] ;
G = [(1, 3)] ;
G = [(2, 3)] ;
G = [(1, 2), (1, 3)] ;
G = [(1, 2), (2, 3)] ;
G = [(1, 3), (2, 3)] ;
G = [(1, 2), (1, 3), (2, 3)] ;
false.
The differences between all the graphs being generated (A006125) vs the desired graphs (A001349) .
A006125 A001349 Extraneous
0 1 - 1 = 0
1 1 - 1 = 0
2 2 - 1 = 1
3 8 - 2 = 6
4 64 - 6 = 58
5 1024 - 21 = 1003
6 32768 - 112 = 32656
7 2097152 - 853 = 2096299
8 268435456 - 11117 = 268424339
9 68719476736 - 261080 = 68719215656
10 35184372088832 - 11716571 = 35184360372261
11 36028797018963968 - 1006700565 = 36028796012263403
12 73786976294838206464 - 164059830476 = 73786976130778375988
13 302231454903657293676544 - 50335907869219 = 302231454853321385807325
14 2475880078570760549798248448 - 29003487462848061 = 2475880078541757062335400387
15 40564819207303340847894502572032 - 31397381142761241960 = 40564819207271943466751741330072
Using geng and listg
Both of these programs are among many others are included in the nauty and Traces download link on the home page. (User's guide)
The programs are written in C and make use of make and can run on Linux. Instead of using Cygwin on Windows, WSL can be installed instead.
The source code can be downloaded using
wget "http://pallini.di.uniroma1.it/nauty26r11.tar.gz"
then
tar xvzf nauty26r11.tar.gz
cd nauty26r11
./configure
make
nauty generates output in graph6 format by default but can be converted to list of edges using listg, e.g.
eric#WINDOWS-XYZ:~/nauty26r11$ ./geng 3 | ./listg -e
>A ./geng -d0D2 n=3 e=0-3
>Z 4 graphs generated in 0.00 sec
Graph 1, order 3.
3 0
Graph 2, order 3.
3 1
0 2
Graph 3, order 3.
3 2
0 2 1 2
Graph 4, order 3.
3 3
0 1 0 2 1 2
Useful options for the programs
geng
-help : options
-c : only write connected graphs (A001349)
-u : do not output any graphs, just generate and count them
Example
eric#WINDOWS-ABC:~/nauty26r11$ ./geng -c -u 10
>A ./geng -cd1D9 n=10 e=9-45
>Z 11716571 graphs generated in 5.09 sec
Notice that 11716571 is the size for 10 from A001349
How to create file on Windows using WSL
Since WSL can access the Windows file system it is possible to direct the output from WSL commands to a Windows file, e.g.
touch /mnt/c/Users/Eric/graphs.txt
The touch step is not needed, but I use it to create an empty file first then verify that it is there on Windows to ensure I have typed the path correctly.
Here is an example that creates the graph edge list for A001349 in the users directory.
.geng -c 1 | .listg -e >> /mnt/c/Users/Eric/graphs.txt
.geng -c 2 | .listg -e >> /mnt/c/Users/Eric/graphs.txt

In the SWI-Prolog you can store values in global vars:
backtrackable b: b_setval, b_getval
not backtrackable nb: nb_setval, nb_getval
besides using dynamic predicates: assert/retract.
item(1).
item(1).
item(2).
item(3).
item(1).
item(7).
item(1).
item(4).
Solution 1 using regular list
gen_test(Item) :-
nb_setval(sofar, []),
item(Item), % generate
once(
(
nb_getval(sofar, SoFar),
(Item > 1, \+ member(Item, SoFar)), % test custom + on membership in earlier tests
nb_setval(sofar, [Item | SoFar])
)
;
true
),
fail; true.
Solution 2 using open list
gen_test1(Item) :-
(
item(Item), % generate
Item > 1, lookup(Item, SoFar, ItemExists)),
ItemExists == true -> fail; true
);
append(SoFar, [], SoFar ), % stubbing open list
nb_setval(sofar, Sofar).
lookup( I, [ I | _ ], true).
lookup( I, [ _ | L ], false) :-
var( L ); L == [].
lookup( I, [ _ | L ] ItemExists ):-
lookup( I, L, ItemExists ).

Related

How to count nth prime in prolog

I'm quite new to prolog and I am trying to write a predicate which gives the value of nth prime number and it looks like nth_prime(N, Prime) .
I have already done the function that counts if the number is prime or not
div(X, Y):- 0 is X mod Y.
div(X, Y):- X>Y+1, Y1 is Y+1, div(X, Y1).
prime(2):- true.
prime(X):- X<2, false.
prime(X):- not(div(X, 2)).
I don't understand what is my next step, and how I should count which prime belong to N.
Your code is a bit unusual for prolog but (with the exception of prime(1)) it works.
Here is a solution for your predicate:
nextprime(N,N):-
prime(N),
!.
nextprime(P, Prime):-
PP is P+1,
nextprime(PP,Prime).
nthprime(1, 2).
nthprime(N, Prime):-
N>1,
NN is N-1,
nthprime(NN, PrevPrime),
PP is PrevPrime+1,
nextprime(PP, Prime).
?- nthprime(1,P).
P = 2 ;
false.
?- nthprime(2,P).
P = 3 ;
false.
?- nthprime(3,P).
P = 5 ;
false.
It works as follows: It is known that the first prime number is 2 (nthprime(1, 2).). For every other number N larger than 1, get the previous prime number (nthprime(NN, PrevPrime)), add 1 until you hit a prime number. The add 1 part is done through a help predicate nextprime/2: for a given number P it will check if this number is a prime. If yes, it returns this number, otherwise it will call itself for the next higher number (nextprime(PP,Prime)) and forwards the output. The bang ! is called a cut which cuts the other choice branches. So if you once hit a prime, you can not go back and try the other path.
To test it you can ask ?- nthprime(N,P). for a given N. Or to check multiple answers at once, let's introdice a helperpredicate nthprimeList/2 which calls nthprime/2 for every item in the firstlist and puts the "output" into a list:
nthprimeList([],[]).
nthprimeList([N|TN],[P|TP]):-
nthprime(N,P),
nthprimeList(TN,TP).
?- nthprimeList([1,2,3,4,5,6,7,8,9],[P1,P2,P3,P4,P5,P6,P7,P8,P9]).
P1 = 2,
P2 = 3,
P3 = 5,
P4 = 7,
P5 = 11,
P6 = 13,
P7 = 17,
P8 = 19,
P9 = 23;
false.
Using your definitions, we define the following to count up and test all numbers from 2 and up, one after another:
nth_prime(N, Prime):-
nth_prime(N, Prime, 1, 2). % 2 is the candidate for 1st prime
nth_prime(N, P, I, Q):- % Q is I-th prime candidate
prime(Q)
-> ( I = N, P = Q
; I1 is I+1, Q1 is Q+1, nth_prime(N, P, I1, Q1)
)
; Q1 is Q+1, nth_prime(N, P, I, Q1).
Testing:
30 ?- nth_prime(N,P).
N = 1,
P = 2 ;
N = 2,
P = 3 ;
N = 3,
P = 5 ;
N = 4,
P = 7 ;
N = 5,
P = 11 .
31 ?- nth_prime(N,P), N>24.
N = 25,
P = 97 ;
N = 26,
P = 101 ;
N = 27,
P = 103 .
32 ?- nth_prime(N,P), N>99.
N = 100,
P = 541 ;
N = 101,
P = 547 ;
N = 102,
P = 557 .

Prolog: Find Minimum again and again until the list is empty

How can I repeatedly extract minimum number until the list is empty?
I want to find a minimum number, then exclude it from the original list, then find a minimum again and again, until the list becomes empty.
Input:
?- Find_Minimum([2, 1, 4, 3, 5], C)
Output:
C = 1
C = 2
C = 3
C = 4
C = 5
False
Here a simple solution using sort/2 (in SWI):
minimum(L,E):-
sort(L,LSorted),
pick(LSorted,E).
pick([H|_],H).
pick([_|T],E):-
pick(T,E).
?- minimum([2,1,4,3,5],E).
E = 1
E = 2
E = 3
E = 4
E = 5
false
Keep in mind that sort/2 removes duplicates. If you want to keep them use for instance msort/2 (in SWI). For an even simpler solution you can use member/2:
minimum(L,E):-
sort(L,LSorted),
member(E,LSorted).
?- minimum([2,1,4,3,5],E).
E = 1
E = 2
E = 3
E = 4
E = 5
I would had written - more or less - the same answer as #damianodamiano (+1), but tried nevertheless to code something more 'direct' than sorting. It turns out the outcome is rather technical...
:- module(minext, [minext/2,minext/3]).
minext(L,M) :-
minext(L,T,R),
( M=T
; minext(R,M)
).
minext(L,X,R) :-
select(X,L,R),
\+((
member(Y,R),
Y<X
)), !.

Prolog - Multiples of a Number Below an Upper Limit

I am currently making a program in Prolog that will calculate all of the multiples (including itself) of a number, that do not exceed the value of another number. I was testing with the query below:
?- multiples(4,12,R,0)
This query would list all multiples of 4 that are less than or equal to 12 eg. 4, 8, 12. The R would return the result and 0 is where I was intending to implement a counter that would count up for each multiplication eg. 4*1,4*2,4*3. I am stuck and I am not sure if it would be a better design to simply add the multiples and check if it is below the upper bound or if it can be done with a counter or accumulator.
multiples(N,U,R,Ctr) :-
N =< U,
R is Ctr * N,
R =< U,
increment(Ctr,Ctr2),
multiples(N,U,R,Ctr2).
increment(Num, Num1) :-
Num1 is Num+1.
I believe my program is failing at the recursive step of calling multiples from within itself. I know that recursion needs a base case to allow it to exit, but I am completely stuck here and would appreciate some direction.
The problem with you approach is that there is no basecase: indeed your algorithm will always produce false. It will unify R with N, then do the recursion and that recursion will try to unify R with 2*N which will fail.
Well an idea could be to use an accumulator to which you add the delta each time. Something like:
multiples(N,U,R) :-
multiples(N,N,U,R).
multiples(_,C,U,C) :-
C =< U.
multiples(N,C,U,R) :-
C =< U,
C1 is C+N,
multiples(N,C1,U,R).
So here we call multiples(3,12,R). and it will result in:
?- multiples(4,12,R).
R = 4 ;
R = 8 ;
R = 12 ;
false.
CLP(FD) is very helpful here:
:- use_module(library(clpfd)).
multiple(Multiplicand, Max, Multiple) :-
MaxMultiplier #= Max // Multiplicand,
label([MaxMultiplier]),
Multiplier in 1 .. MaxMultiplier,
Multiple #= Multiplier * Multiplicand,
label([Multiple]).
?- multiple(4, 12, M).
M = 4 ;
M = 8 ;
M = 12.
?-
With CLP(FD) in this case, you can also query with the first argument as a variable:
|?- multiple(N, 12, 8).
N = 8 ;
N = 4 ;
N = 2 ;
N = 1.
Or both the multiplier and result:
?- multiple(N, 4, M).
N = M, M = 3 ;
N = M, M = 4 ;
N = M, M = 2 ;
N = 2,
M = 4 ;
N = M, M = 1 ;
N = 1,
M = 2 ;
N = 1,
M = 3 ;
N = 1,
M = 4.
?-
If you want to collect them in a list, you can use findall/3:
?- findall(Multiple, multiple(4, 12, Multiple), Multiples).
Multiples = [4, 8, 12].
?-

How to find two numbers where a restriction is applied

Let's say that I want to find two numbers where the sum of these are 8, are from 1-9 and must be different(it is obvious that these numbers are (7,1),(6,2),etc).So I wrote.
dif_list([H|T]):- \+ member(H,T),dif_list(T).
dif_list([]).
check1_9([H|T]):-H>=1,H=<9,check1_9(T).
check1_9([]).
find_number([A,B],N) :- N =:= A+B,dif_list([A,B]),check1_9([A,B]).
Afterwards I will ask prolog
?-find_number([A,B],8).
ERROR: =:=/2: Arguments are not sufficiently instantiated
My goal is that prolog will print for me the results.For example:
?-find_number([A,B],8).
A = 7,
B = 1 ;
A = 6,
B = 2 ;
...
The best way to handle this kind of problem in Prolog is to use the CLP(FD) library:
:- [library(clpfd)].
sum_of(A, B, Sum) :-
A #> 0,
B #> 0,
A + B #= Sum.
?- sum_of(A, B, 8), label([A, B]).
A = 1,
B = 7 ;
A = 2,
B = 6 ;
A = 3,
B = 5 ;
A = B, B = 4 ;
A = 5,
B = 3 ;
A = 6,
B = 2 ;
A = 7,
B = 1.
?-
If you want the addends to be unique, you can further constrain it:
sum_of(A, B, Sum) :-
A #> 0,
B #>= A,
A + B #= Sum.
There's really no need to use a list to manage the variables A and B, but you can if you wish: sum_of([A,B], Sum).
Prolog is not that declarative: there are indeed answer set programming (ASP) or constraint logic programming (clp) languages where you can simply define a set of constraints and a finite domain solver aims to solve it (but these will take considerable time).
I would suggest that you define your program as follows:
find_number(A,B,N) :-
member(A,[1,2,3,4,5,6,7,8,9]),
member(B,[1,2,3,4,5,6,7,8,9]),
N is A+B,
A \= B.
Here member/2 will instantiate A and B to values that are provided by the list, so 1..9, next you use is/2 to calculate the sum and verify that the sum is equal to N. You can only call N is A+B if A and B are given a proper value. Finally we say A \= B (A is not equal to B).
When you run this predicate, it produces:
?- find_number(A,B,8).
A = 1,
B = 7 ;
A = 2,
B = 6 ;
A = 3,
B = 5 ;
A = 5,
B = 3 ;
A = 6,
B = 2 ;
A = 7,
B = 1 ;
false.
You can however also query with A and B already filled in, or one of them filled in, or where the sum is left open. So:
?- find_number(A,2,8).
A = 6 ;
false.
or:
?- find_number(A,2,N).
A = 1,
N = 3 ;
A = 3,
N = 5 ;
A = 4,
N = 6 ;
A = 5,
N = 7 ;
A = 6,
N = 8 ;
A = 7,
N = 9 ;
A = 8,
N = 10 ;
A = 9,
N = 11 ;
false.

clpfd - generate the list of all integers between 5 and 10

I'm working with SWI-Prolog to get clpfd generate the list of all distinct integers between 5 and 10:
q1(Answer) :-
length(Xs, Answer),
Xs ins 0..20,
chain(Xs, #<),
maplist(q1constraints, Xs).
q1constraints(X) :-
X #>= 5,
X #=< 10.
Kind of works, but generates a solution for each of the lengths 0, 1, ... 6 and then hangs seeking a solution of length 7:
?- q1(Answer).
Answer = 0 ;
Answer = 1 ;
Answer = 2 ;
Answer = 3 ;
Answer = 4 ;
Answer = 5 ;
Answer = 6 ;
<hangs>
Is there a good way to generate the list of all integers that satisfy the desired constraints?
Your question is not that clear to me. But I will try:
?- length(Xs,6), Xs ins 5..10, chain(Xs,#<), Xs = [5|_], last(Xs, 10).
Xs = [5,6,7,8,9,10].
Note that with these elements, it is necessary to fix the length of the list first. Otherwise:
?- length(Xs,N), Xs ins 5..10, chain(Xs,#<), Xs = [5|_], last(Xs, 10).
Xs = [5,10], N = 2
; Xs = [5,_A,10], N = 3, _A in 6..9
; Xs = [5,_A,_B,10], N = 4, _A#=<_B+ -1, _A in 6..8, _B in 7..9
; Xs = [5,_C,_A,_B,10], N = 5, _A#=<_B+ -1, _C#=<_A+ -1, _A in 7..8, _C in 6..7, _B in 8..9
; Xs = [5,6,7,8,9,10], N = 6
; loops.
In fact, even the (ins)/2 is not needed:
?- length(Xs,6), chain(Xs,#<), Xs = [5|_], last(Xs, 10).
Xs = [5,6,7,8,9,10].
(In newer versions of clpfd called clpz the argument order of chain/2` is reversed adhering to the common argument ordering.)
Here is what your program does:
Generate a list of increasing lengths, starting with an empty list
For each element X in the list, pose a constraint that X is in [0, 20]
For the whole list, pose a constraint that values are strictly increasing in magnitude
For each element in the list, pose an additional constraint that X is in [5, 10].
You then ask for the length of the generated list.
There are 6 values that are in [0, 20] and in [5,10]: 5, 6, 7, 8, 9, 10. For the empty list you generate first, there are no constrained variables; for the list with 1 variable, there would be 6 possible values of the variable, but you don't ask for these values, only for the length of the list; for the list with 2 variables, you will have 5 possible combinations: {5,6}, {6,7}, ..., {9,10}, but again, you don't ask for them, just for the length of the list.
Eventually, you get to list with 7 values. Since there are only 6 values that each element could have, there are no solutions.
So what is your goal here? Maybe you should try and explain better. To get all values between 5 and 10 by backtracking, you could say: between(5, 10, X), or, with CLPFD, X in 5..10, label([X]). If it is neither of these, you need to re-write your question.
If you want the total number of even integers in 5..10 (SPOILER: there are 3 of them!):
?- aggregate(count, X^(X in 5..10, X mod 2 #= 0, indomain(X)), Answer).
Answer = 3.
Breaking it down:
X in 5..10, X mod 2 #= 0 just constrains X to be an even integer between 5 and 10:
?- X in 5..10, X mod 2 #= 0.
X in 6..10,
X mod 2#=0.
indomain(X) does the actual search, succeeding once for each feasible value of X:
?- X in 5..10, X mod 2 #= 0, indomain(X).
X = 6 ;
X = 8 ;
X = 10.
X^(...) existentially quantifies X within the parentheses, limiting its scope. If we instead leave it as a free variable, aggregation will respect it:
?- aggregate(count, (X in 5..10, X mod 2 #= 0, indomain(X)), Answer).
X = 6,
Answer = 1 ;
X = 8,
Answer = 1 ;
X = 10,
Answer = 1.

Resources