One of the recent Advent of code challenges tasks me with solving for the smallest amount of input material that I can use to apply a given set of reactions and get 1 unit of output material.
For example, given
10 ORE => 10 A
1 ORE => 1 B
7 A, 1 B => 1 C
7 A, 1 C => 1 D
7 A, 1 D => 1 E
7 A, 1 E => 1 FUEL
We need 31 total ore to make 1 fuel (1 to produce a unit of B, then 30 to make the requisite 28 A).
This year, I've been trying to push my programming-language horizons, so I've done most of the challenges in SML/NJ. This one seems—seemed—like a good fit for Prolog, given the little I know about it: logic programming, constraint solving, etc.
I haven't, however, been able to successfully model the constraints.
I started by turning this simple example into some facts:
makes([ore(10)], a(10)).
makes([ore(1)], b(1)).
makes([a(7), b(7)], c(1)).
makes([a(7), c(1)], d(1)).
makes([a(7), d(1)], e(1)).
makes([a(7), e(1)], fuel(1)).
To be honest, I'm not even sure if the list argument is a good structure, or if the functor notation (ore(10)) is a good model either.
Then I wanted to build the rules that allow you to say, e.g., 10 ore makes enough for 7 a:
% handles the case where we have leftovers?
% is this even the right way to model all this... when we have leftovers, we may
% have to use them in the "reaction"...
makes(In, Out) :-
Out =.. [F,N],
Val #>= N,
OutN =.. [F,Val],
makes(In, OutN).
This works1, but I'm not sure it's going to be adequate, since we may care about leftovers (this is a minimization problem, after all)?
I'm stuck on the next two pieces though:
I can ask what makes 7 A and get back 10 ore, but I can't ask what is enough for 20 A: how do I write a rule which encodes multiplication/integer factors?
I can say that 7 A and 1 E makes 1 fuel, but I can't state that recursively: that is, I cannot state that 14 A and 1 D also make 1 fuel. How do I write the rule that encodes this?
I'm open to alternate data encodings for the facts I presented—ultimately, I'll be scripting the transformation from Advent's input to Prolog's facts, so that's the least of my worries. I feel that if I can get this small example working, I can solve the larger problem.
?- makes(X, a(7)). gives back X=[ore(10)] infinitely (i.e., if I keep hitting ; at the prompt, it keeps going). Is there a way to fix this?
Not a direct answer to your specific question but my first thought on this problem was to use chr in Prolog.
I then thought I would forward chain from fuel to the amount of ore I need.
The basic constraints:
:- chr_constraint ore/1, a/1, b/1,c/1, ab/1, bc/1, ca/1, fuel/0.
a(1),a(1) <=> ore(9).
b(1),b(1),b(1) <=> ore(8).
c(1),c(1),c(1),c(1),c(1) <=> ore(7).
ab(1) <=> a(3),b(4).
bc(1) <=> b(5),c(7).
ca(1) <=> c(4),a(1).
fuel <=> ab(2),bc(3),ca(4).
%Decompose foo/N into foo/1s
a(X) <=> X>1,Y#=X-1|a(Y),a(1).
b(X) <=> X>1,Y#=X-1|b(Y),b(1).
c(X) <=> X>1, Y#=X-1 | c(Y),c(1).
ab(X) <=> X>1, Y#=X-1|ab(Y),ab(1).
bc(X) <=> X>1,Y#=X-1| bc(Y),bc(1).
ca(X) <=> X>1, Y#= X-1| ca(Y),ca(1).
ore(X)<=>X >1, Y #= X -1 |ore(Y),ore(1).
%aggregation (for convenience)
:- chr_constraint ore_add/1, total_ore/1.
total_ore(A), total_ore(Total) <=> NewTotal #= A + Total, total_ore(NewTotal).
ore_add(A) ==> total_ore(A).
ore(1) <=> ore_add(1).
Query:
?-fuel.
b(1),
b(1),
c(1),
c(1),
ore_add(1),
ore_add(1),
...
total_ore(150).
Then you would need to add a search procedure to eliminate the two b/1s and two c/1s.
I have not implemented this but:
?-fuel,b(1),c(3).
ore_add(1),
...
total_ore(165)
This has only ore_add/1 constraints and is the correct result.
In the example there are no "alternative" path and no multiple "ore sources", so coding the example up in a very non-flexible way using Prolog can be done like this:
need(FUEL,OREOUT) :- need(FUEL,0,0,0,0,0,0,OREOUT).
need(FUEL,E,D,C,A,B,ORE,OREOUT) :- FUEL > 0, A2 is 7*FUEL+A, E2 is FUEL+E, need(0, E2, D, C, A2, B, ORE,OREOUT).
need(0,E,D,C,A,B,ORE,OREOUT) :- E > 0, A2 is 7*E+A, D2 is E+D, need(0, 0, D2, C, A2, B, ORE,OREOUT).
need(0,0,D,C,A,B,ORE,OREOUT) :- D > 0, A2 is 7*D+A, C2 is D+C, need(0, 0, 0, C2, A2, B, ORE,OREOUT).
need(0,0,0,C,A,B,ORE,OREOUT) :- C > 0, A2 is 7*C+A, B2 is C+B, need(0, 0, 0, 0, A2, B2, ORE,OREOUT).
need(0,0,0,0,A,B,ORE,OREOUT) :- X is A + B, X > 0, ORE2 is ORE + (A + 9)//10 + B, need(0, 0, 0, 0, 0, 0, ORE2,OREOUT).
need(0, 0, 0, 0, 0, 0, ORE, ORE).
Then
?- need(1011,ORE).
ORE = 3842
But this is just a silly and inelegant attempt.
There is a major general problem lurking thereunder, which includes parsing the arbitrarily complex reaction directed acyclic graph and building an appropriate structure. The good think is that it is a DAG, so one cannot generate an "earlier ingredient" from a "later one".
While making coffee, this is clearly something for the CLP(FD) engine.
If we have directed acyclic graph of reactions with
FUEL node on the right of the graph and
nodes for intermediate products IP[i] (i in 0..n) in between, with possibly
several FUEL nodes, i.e. several ways generating FUEL: FUEL[0] ... FUEL[v] and possibly
several nodes for intermediate products IP[i], i.e. several ways of creating intermediate product IP[i>0]: IP[i,1] ... IP[i,ways(i)] and
IP[0] identified with ORE on the left side of the graph
with the last two points giving us a way of choosing a strategy for the product mix, then:
FUEL_NEEDED = mix[0] * FUEL[0] + ... + mix[v] * FUEL[v]
with everything in the above a variable
and the following given by the problem statement, with FUEL[0] ... FUEL[v] variables and the rest constants:
out_fuel[0] * FUEL[0] = ∑_j ( IP[j] * flow(IPj->FUEL0) )
⋮
out_fuel[v] * FUEL[v] = ∑_j ( IP[j] * flow(IPj->FUELv) )
and for each IP[i>0], with the IP[i] variables and the rest constants:
out_ip[i] * IP[i] = ∑_j≠i ( IP[j] * flow(IPj->IPi) )
in case of a several ways to generate IP[i], we mix (this is like introducing a graph node for the mix of IP[i] from its possible ways IP[i,j]):
out_ip[i] * IP[i] = ∑_j(0..ways(i)) ( IP[i,j] * mix[i,j] )
out_ip[i,1] * IP[i,1] = ∑_j≠i ( IP[j] * flow(IP[j]->IP[i,1]) )
⋮
out_ip[i,ways(i)] * IP[i,ways(i)] = ∑_j≠i ( IP[j] * flow(IP[j]->IP[i,ways(i)]) )
and IP[0] (i.e. ORE) a free variable to be minimized.
You see an underspecified linear programming problem appearing here, with a matrix having zeroes below the diagonal because it's a DAG, but it contains variables to be optimized in the matrix itself. How to attack that?
Related
Does anybody know where to find a Prolog algorithm for computing the probability of a disjunction for N dependent events? For N = 2 i know that P(E1 OR E2) = P(E1) + P(E2) - P(E1) * P(E2), so one could do:
prob_disjunct(E1, E2, P):- P is E1 + E2 - E1 * E2
But how can this predicate be generalised to N events when the input is a list? Maybe there is a package which does this?
Kinds regards/JCR
The recursive formula from Robert Dodier's answer directly translates to
p_or([], 0).
p_or([P|Ps], Or) :-
p_or(Ps, Or1),
Or is P + Or1*(1-P).
Although this works fine, e.g.
?- p_or([0.5,0.3,0.7,0.1],P).
P = 0.9055
hardcore Prolog programmers can't help noticing that the definition isn't tail-recursive. This would really only be a problem when you are processing very long lists, but since the order of list elements doesn't matter, it is easy to turn things around. This is a standard technique, using an auxiliary predicate and an "accumulator pair" of arguments:
p_or(Ps, Or) :-
p_or(Ps, 0, Or).
p_or([], Or, Or).
p_or([P|Ps], Or0, Or) :-
Or1 is P + Or0*(1-P),
p_or(Ps, Or1, Or). % tail-recursive call
I don't know anything about Prolog, but anyway it's convenient to write the probability of a disjunction of a number of independent items p_m = Pr(S_1 or S_2 or S_3 or ... or S_m) recursively as
p_m = Pr(S_m) + p_{m - 1} (1 - P(S_m))
You can prove this by just peeling off the last item -- look at Pr((S_1 or ... or S_{m - 1}) or S_m) and just write that in terms of the usual formula, writing Pr(A or B) = Pr(A) + Pr(B) - Pr(A) Pr(B) = Pr(B) + Pr(A) (1 - Pr(B)), for A and B independent.
The formula above is item C.3.10 in my dissertation: http://riso.sourceforge.net/docs/dodier-dissertation.pdf It is a simple result, and I suppose it must be an exercise in some textbooks, although I don't remember seeing it.
For any event E I'll write E' for the complementary event (ie E' occurs iff E doesn't).
Then we have:
P(E') = 1 - P(E)
(A union B)' = A' inter B'
A and B are independent iff A' and B' are independent
so for independent E1..En
P( E1 union .. union En ) = 1 - P( E1' inter .. inter En')
= 1 - product{ i<=i<=n | 1 - P(E[i])}
The distance between a long sequence and a short sequence, is the minimum distance between the short sequence and any subsequence of the long sequence that is the same length as the short sequence.
The distance I am using is I think the Manhattan distance. (But this should be unimportant as I would like to be able to change distance functions).
This first version shows a naive implementation without early abandon. I generate all subsequence of the same length, map these to find the distance between them and the short sequence and then use aggregate/3 to find the min.
abs(X,Y,Z):-
Z is abs(X-Y).
seq_seq_absdis(Seq1,Seq2,Dis):-
same_length(Seq1,Seq2),
maplist(abs,Seq1,Seq2,Dislist),
sumlist(Dislist,Dis).
seq_subseq(List1,List2):-
append(List2,_,List1),
dif(List2,[]).
seq_subseq([_|T],Subseq):-
seq_subseq(T,Subseq).
smallseq_largeseq_dis(Sseq,Lseq,Dis):-
findall(Subseq, (same_length(Subseq,Sseq),seq_subseq(Lseq,Subseq)),Subseqs),
maplist(seq_seq_absdis(Sseq),Subseqs,Distances),
aggregate(min(D),member(D,Distances),Dis).
Example query:
?-smallseq_largeseq_dis([1,2,4],[1,2,3,1,2,5],Dis).
Dis = 1
This next version should be more efficient, as it will abandon calculating the distance between a subsequence of the long sequence and the short sequence once the distance is over the minimum already found.
ea_smallseq_largeseq_dis(Sseq,Lseq,Subseq,Dis):-
retractall(best(_,_)),
assert(best(initial,10000)),
findall(Subseq-Dis, ea_smallseq_largeseq_dis_h(Sseq,Lseq,10000,Subseq,Dis),Pairs),
append(_,[Subseq-Dis|[]],Pairs).
ea_smallseq_largeseq_dis_h(Sseq,Lseq,BestSofar1,Subseq,Dis):-
same_length(Sseq,Subseq),
seq_subseq(Lseq,Subseq),
best(_,BestSofar2),
( ( BestSofar2 < BestSofar1) ->
accumulate_dis(Sseq,Subseq,BestSofar2,Dis),
retractall(best(_,_)),
assert(best(Subseq,Dis))
;(
accumulate_dis(Sseq,Subseq,BestSofar1,Dis),
retractall(best(_,_)),
assert(best(Subseq,Dis))
)
).
accumulate_dis(Seq1,Seq2,Best,Dis):-
accumulate_dis(Seq1,Seq2,Best,Dis,0).
accumulate_dis([],[],_Best,Dis,Dis).
accumulate_dis(Seq1,Seq2,Best,Dis,Ac):-
Seq1=[H1|T1],
Seq2=[H2|T2],
abs(H1,H2,Dis1),
Ac1 is Dis1 + Ac,
Ac1 <Best,
accumulate_dis(T1,T2,Best,Dis,Ac1).
Query:
?-ea_smallseq_largeseq_dis([1,2,3],[1,2,4,5,6,7,8,1,2,3],Subseq,Dis).
Dis = 0,
Subseq = [1, 2, 3]
But in this I have used assert and retract so I want to have a version which does the same algorithm but with out these. I think I should be able to do this with a dcg with semicontext notation but find it hard to grasp... how do I keep track of the subsequences I am generating by backtracking and at the same time the 'state' of the minimum distance found so far?
The problem I have..
seq_subseq/2 is generating the sub-sequences by back tracking.
The first subseq tested needs to be set to the min distance.
I then want to loop, so back track to generate another sequence. But to back track I have to fail. But then I cant bring back the min distance so far to check on the next sequence.
If I don't want to use backtracking, I think I need to define a state transition predicate for generating the sub-sequences in order.
At the moment
? seq_subseq([1,2,3,4],X).
X = [1]
X = [1, 2]
X = [1, 2, 3]
X = [1, 2, 3, 4]
X = [2]
X = [2, 3]
X = [2, 3, 4]
X = [3]
X = [3, 4]
X = [4]
So I think I need to define a relation:
subseq0_seq_subseq1(Subseq0,Seq,Subseq1)
That would work like:
?-subseq0_seq_subseq1([1,2,3,4],[1,2,3,4],Subseq1).
Subseq1 = [2].
and
?-subseq0_seq_subseq1([1,2,3],[1,2,3,4],Subseq1).
Subseq1 = [1,2,3,4].
But I need to do this in an efficient way.
Update- Thanks to the answer from Mat I now have this, which is a great improvement I think. Can anyone see any further improvements to this? I have a double nested -> structure and a ! in the accumulate_dis/4 definition both of which seem ugly. I have also made it return the sub-sequence of the long-sequence which is the shortest distance away from the short sequence.
It needs to work with non integers so clpfd is not appropriate I think.
abs(X,Y,Z):-
Z is abs(X-Y).
list_subseq_min(Ls, Subs, Min,BestSeq1) :-
prefix_dist(Ls, Subs, 1000, Front, D0),
BestSeq0=Front,
min_sublist(Ls, Subs,BestSeq0,BestSeq1, D0, Min).
prefix_dist(Ls, Ps, Best,Front,D) :-
same_length(Front, Ps),
append(Front, _, Ls),
accumulate_dis(Front, Ps, Best, D).
min_sublist(Ls0, Subs, BestSeq0,BestSeq2, D0, D) :-
( prefix_dist(Ls0, Subs, D0, Front,D1) ->
min_list([D0,D1],D2),
Ls0 = [_|Ls],
( D0 < D1 ->
BestSeq1 =BestSeq0
;
BestSeq1 =Front
),
min_sublist(Ls, Subs, BestSeq1,BestSeq2, D2, D)
; D = D0,BestSeq0 =BestSeq2
).
accumulate_dis(Seq1,Seq2,Best,Dis):-
accumulate_dis(Seq1,Seq2,Best,Dis,0),!.
accumulate_dis([],[],_Best,Dis,Dis).
accumulate_dis(Seq1,Seq2,Best,Dis,Ac):-
Seq1=[H1|T1],
Seq2=[H2|T2],
abs(H1,H2,Dis1),
Ac1 is Dis1 + Ac,
Ac1 <Best,
accumulate_dis(T1,T2,Best,Dis,Ac1).
accumulate_dis(Seq1,Seq2,Best,Dis):-Dis is Best+1.
query:
?- list_subseq_min([2.1,3.4,4,1.1,2,4,10,12,15],[1,2,3],D,B).
D = 1.1,
B = [1.1, 2, 4].
One important note: You should have clearified that you are talking about the Manhatten distance between lists. This was only clear from your code, whereas your wording can easily lead readers to assume you are talking about the edit distance.
Here is a solution that simply walks the list, keeps track of the minimum, and eventually yields the found minimum.
list_subseq_min(Ls, Subs, Min) :-
prefix_dist(Ls, Subs, D0),
min_sublist(Ls, Subs, D0, Min).
absdiff(X, Y, Z):- Z #= abs(X-Y).
lists_dist(Ls1, Ls2, D) :-
maplist(absdiff, Ls1, Ls2, Ds),
sum(Ds, #=, D).
prefix_dist(Ls, Ps, D) :-
same_length(Front, Ps),
append(Front, _, Ls),
lists_dist(Front, Ps, D).
min_sublist(Ls0, Subs, D0, D) :-
( prefix_dist(Ls0, Subs, D1) ->
D2 #= min(D0,D1),
Ls0 = [_|Ls],
min_sublist(Ls, Subs, D2, D)
; D #= D0
).
Example query and its result:
?- list_subseq_min([1,2,3,1,2,5], [1,2,4], D).
D = 1.
It's quite straight-forward, and since the bookkeeping is limited to only one predicate, using semicontext notations does not really pay off. It is especially useful to use semicontext notation—and DCGs in general—when what is being described spans different rules and communication between them would otherwise be harder.
The running time is in O(N×M).
And now the point, which I leave as an exercise: Modify this solution to prune earlier, if the previously found minimum is already exceeded. Do so in a pure way, or at least as pure as possible: assertz/1 and friends are definitely out of the question, since they prevent your testing these predicates in isolation. Pass around arguments and build the distance more incrementally! This may help you to improve the average running time, though of course not the worst case complexity.
It is for this passing around states between different clauses that semicontext notation may become useful at last.
EDIT: Very well, you have implemented a solution that does the pruning. I will now also show mine. I will reuse the auxiliary predicates absdiff/3 and lists_dist/3 from above, and the following additional auxiliary predicate:
same_length_prefix(Ls, Ps, Front) :-
same_length(Front, Ps),
append(Front, _, Ls).
list_subseq_min/3 is now slightly different:
list_subseq_min(Ls, Subs, Min) :-
same_length_prefix(Ls, Subs, Front),
lists_dist(Front, Subs, D0),
phrase(min_sublist(Ls, Subs), [D0-Front], [Min-_]).
And now the point: min_sublist//2 is a DCG nonterminal that concisely describes the main idea of the algorithm:
min_sublist(Ls0, Subs) -->
( front(Ls0, Subs) ->
{ Ls0 = [_|Ls] },
min_sublist(Ls, Subs)
; []
).
From this description, it is very clear that we are considering the list element by element. It uses fewer (explicit) arguments than previously. The additional two arguments are implicitly passed around as a pair D-Front, which keeps track of the best distance and subsequence found so far. Note how DCG notation exposes the core of the computation, and hides what is not relevant at this place.
The rest is quite self-explanatory, and is analogous to the pruning you have implemented. I highlight the single use of semicontext notation in this program, which lets us express any change of the optimal sequence found so far.
front(Ls, Subs), [D-Front] -->
[Current],
{ same_length_prefix(Ls, Subs, Front1),
capped_dist(Front1, Subs, Current, 0-Front1, D-Front) }.
capped_dist([], [], _, DF, DF).
capped_dist([L|Ls], [P|Ps], Current, D1-Front1, DF) :-
absdiff(L, P, D2),
D3 #= D1 + D2,
Current = D0-_,
( D3 #> D0 -> DF = Current
; capped_dist(Ls, Ps, Current, D3-Front1, DF)
).
I can't bring myself to accept the nastiness and primitiveness of contemporary floating-point numbers, so I have retained the integer arithmetic and simply multiply all numbers you show so that they become integers:
?- list_subseq_min([21,34,40,11,20,40,100,120,150], [10,20,30], D).
D = 11.
I leave extending this so that it also shows the found subsequence as an easy exercise.
One important note: The capping only affects the calculation of the distance; note in particular that the running time is Θ(N×M) due to the way same_length_prefix/3 is used in front//2! I leave improving this as a slightly harder exercise.
I have an exam coming up and I'm going through past papers to help my understanding.
I came across the following past paper question:
Consider the following queries and answers. Some answers coincide with
what SWI-Prolog would infer whereas others are erroneous. Indicate which
answers are genuine and which ones are fake (no explanation of your answer
is required).
(i) |?- [A, B, C] ins 0 .. 2, A #= B + C.
A = 0..2 B = 0..2 C = 0..2
(ii) |?- A in 0 .. 3, A * A #= A.
A = 0..2
(iii) |?- [A, B] ins -1 .. 1, A #= B.
A = 1 B = 1
(iv) |?- [A, B] ins 0 .. 3, A #= B + 1.
A = 1..3 B = 1..2
I'm struggling to see how each one is either true or false. Would someone be able to explain to me how to figure these out please.
Thank you, really appreciate the help.
The key principle for deciding which answers are admissible and which are not is to look whether the residual program is declaratively equivalent to the original query. If the residual constraints admit any solution that the original query does not, or the other way around, then the answer is fake (or you have found a mistake in the CLP(FD) solver). If the shown answer is not even syntactically valid, then the answer is definitely fake.
Let's do it:
(i) |?- [A, B, C] ins 0 .. 2, A #= B + C.
suggested answer: A = 0..2 B = 0..2 C = 0..2
WRONG! The original query constrains the variables to integers, but this answer is not even a syntactically valid Prolog program.
(ii) |?- A in 0 .. 3, A * A #= A.
suggested answer: A = 0..2
WRONG! The original query constrains A to integers, but according to this residual program, A = 0..2 is a valid solution. The term ..(0, 2) is not an integer.
(iii) |?- [A, B] ins -1 .. 1, A #= B.
suggested answer: A = 1 B = 1
WRONG! Not syntactically valid.
(iv) |?- [A, B] ins 0 .. 3, A #= B + 1.
suggested answer: A = 1..3 B = 1..2
WRONG! Not syntactically valid.
Note that even if all shown answers were syntactically valid and =/2 were replaced by in/2 in the residual goals of (i), (ii) and (iv), these answer would still be all wrong, because you can in each case find solutions that either are not admissible by the original query or the residual goals, but not both. I leave solving these cases as an exercise for you, for example, suppose the respective answers are:
A in 0..2, B in 0..2, C in 0..2.
A in 0..2.
A = 1, B = 1.
A in 1..3, B in 1..2.
and find a witness for each case to show that the residual goals are semantically different from the respective original query.
For example, in case (1), A = B = C = 2 would be a valid solution according to the residual constraints, but obviously the original constraints exclude this solution, because 2 #= 2 + 2 does not hold!
A variable is always restricted to get a value contained in its domain, and arithmetic constrains only reduce the domain of involved variables.
So, try to 'label' all variables - that is, assign values from answers reported domains. Of course, if the arithmetic relation is not satisfied, you can say the answer is faked. Take ii). Does it hold for A=0 ? What about A=2 ?
This 'test' of course doesn't suffice to answer all questions. Some reported domains are narrower. For instance, take iii). Can you see any reason that excludes -1, or 0. If you cannot, you should mark the answer as faked.
I am new to PROLOG and am trying some simple exercises to familiarize myself with it. However I am stuck in making an addition of 2x2matrix with another, more specifically lists within lists.
This is my code, the output using SWI-Prolog is False, and I have no idea why. Any help is appreciated!
matrixAdd([X],[Y],[S]) :- S is X + Y.
matrixAdd([[H|A],[I|B]],[[J|C],[K|D]],[[S1|Sum1],[S2|Sum2]]) :-
S1 = H + J,
S2 = I + K,
matrixAdd([A,B],[C,D],[Sum1,Sum2]).
Elaborating:
?- A = 2 + 3.
A = 2+3.
?- A = 2 + 3, A == 5.
false.
?- A = 2 + 3, A = 5.
false.
?- A is 2 + 3, A =:= 10/2.
A = 5.
?- A is 2 + 3, A = 10/2.
false.
Figure out why you get each of these answers.
Furthermore, think about how you want to represent your matrix. Does it need to be a nested list? For example, it could be something like matrix(dim(2,2), [1,2,3,4]). Then, adding two matrices would be as easy as:
matrix_sum(matrix(D, V1), matrix(D, V2), matrix(D, Sum)) :-
maplist(add, V1, V2, Sum).
add(X, Y, Sum) :-
Sum is X + Y.
(You could get fancy and use a constraint library for the add operation. For example, with library(clpr) you could write {Sum = X+Y} and use the same predicate for addition and for subtraction.)
This uses unification in the head to make sure that the two matrices have the same dimensions, while the maplist take care of V1 and V2 being the same length.
Or you prefer a list of lists. Then, figure out the general predicate that adds lists of lists together (see the other answer!). Now you have a weird mix where you kind of know the magnitude of one dimension in advance, but still attempt to traverse the other dimension. As your code is at the moment, it is your base case that always fails. It should be:
matrixAdd([[],[]],[[],[]],[[],[]]).
(so many lists!) and without any body. Try replacing it in your original code and see what happens.
You first need to know the following:
There is a difference between = and is/2
Check your list syntax [Head|Tail] is different from [Element1,Element2]
Unification of [X] will only work when you pass a list with exactly 1 element. Just like [[A,B],[C,D]] will only match a 2 by 2 matrix. (Note that the elements A,B,.. could be lists as well in that case)
For you hardcode solution, fixing all these issues should work, but I want to leave that to you for now.
matrixAddHardcode([[A1,A2],[A3,A4]],[[B1,B2],[B3,B4]],[[S1,S2],[S3,S4]]) :-
S1 is A1 + B1,
S2 is A2 + B2,
S3 is A3 + B3,
S4 is A4 + B4.
Solution for any X by Y
matrixAddFix([],[],[]).
matrixAddFix([L1|T1],[L2|T2],[S1|TS]) :-
listSum(L1,L2,S1),
matrixAddFix(T1,T2,TS).
listSum([],[],[]).
listSum([H1|T1],[H2|T2],[S1|TS]) :-
S1 is H1+H2,
listSum(T1,T2,TS).
I am working on solving the classic Missionaries(M) and Cannibals(C) problem, the start state is 3 M and 3 C on the left bank and the goal state is 3M, 3C on the right bank. I have complete the basic function in my program and I need to implemet the search-strategy such as BFS and DFS.
Basically my code is learn from the Internet. So far I can successfuly run the program with DFS method, but I try to run with BFS it always return false. This is my very first SWI-Prolog program, I can not find where is the problem of my code.
Here is part of my code, hope you can help me find the problem of it
solve2 :-
bfs([[[3,3,left]]],[0,0,right],[[3,3,left]],Solution),
printSolution(Solution).
bfs([[[A,B,C]]],[A,B,C],_,[]).
bfs([[[A,B,C]|Visisted]|RestPaths],[D,E,F],Visisted,Moves) :-
findall([[I,J,K],[A,B,C]|Visited]),
(
move([A,B,C],[I,J,K],Description),
safe([I,J,K]),
not(member([I,J,K],Visited)
),
NewPaths
),
append(RestPaths,NewPaths,CurrentPaths),
bfs(CurrentPaths,[D,E,F],[[I,J,K]|Visisted],MoreMoves),
Moves = [ [[A,B,C],[I,J,K],Description] | MoreMoves ].
move([A,B,left],[A1,B,right],'One missionary cross river') :-
A > 0, A1 is A - 1.
% Go this state if left M > 0. New left M is M-1
.
.
.
.
.
safe([A,B,_]) :-
(B =< A ; A = 0),
A1 is 3-A, B1 is 3-B,
(B1 =< A1; A1 =0).
I use findall to find all possible path before go to next level. Only the one pass the safe() will be consider as possible next state. The state will not use if it already exist. Since my program can run with DFS so I think there is nothing wrong with move() and safe() predicate. My BFS predicate is changing base on my DFS code, but its not work.
There is a very simple way to turn a depth-first search program into a breadth-first one, provided the depth-first search is directly mapped to Prolog's search. This technique is called iterative deepening.
Simply add an additional argument to ensure that the search will only go N steps deep.
So a dfs-version:
dfs(State) :-
final(State).
dfs(State1) :-
state_transition(State1, State2),
dfs(State2).
Is transformed into a bfs by adding an argument for the depth. E.g. by using successor-arithmetics:
bfs(State, _) :-
final(State).
bfs(State1, s(X)) :-
state_transition(State1, State2),
bfs(State2, X).
A goal bfs(State,s(s(s(0)))) will now find all derivations requiring 3 or less steps. You still can perform dfs! Simply use bfs(State,X).
To find all derivations use natural_number(X), bfs(State,X).
Often it is useful to use a list instead of the s(X)-number. This list might contain all intermediary states or the particular transitions performed.
You might hesitate to use this technique, because it seems to recompute a lot of intermediary states ("repeatedly expanded states"). After all, first it searches all paths with one step, then, at most two steps, then, at most three steps... However, if your problem is a search problem, the branching factor here hidden within state_transition/2 will mitigate that overhead. To see this, consider a branching factor of 2: We only will have an overhead of a factor of two! Often, there are easy ways to regain that factor of two: E.g., by speeding up state_transition/2 or final/1.
But the biggest advantage is that it does not consume a lot of space - in contrast to naive dfs.
The Logtalk distribution includes an example, "searching", which implements a framework for state space searching:
https://github.com/LogtalkDotOrg/logtalk3/tree/master/examples/searching
The "classical" problems are included (farmer, missionaries and cannibals, puzzle 8, bridge, water jugs, etc). Some of the search algorithms are adapted (with permission) from Ivan Bratko's book "Prolog programming for artificial intelligence". The example also includes a performance monitor that can give you some basic stats on the performance of a search method (e.g. branching factors and number of state transitions). The framework is easy to extend, both for new problems and new search methods.
If anyone still interested in this for a python solution please find the following.
For the simplification, count of Missionaries and Cannibals on left is only taken to the consideration.
This is the solution tree.
#M #missionaries in left
#C #cannibals in left
# B=1left
# B=0right
def is_valid(state):
if(state[0]>3 or state[1]>3 or state[2]>1 or state[0]<0 or state[1]<0 or state[2]<0 or (0<state[0]<state[1]) or (0<(3-state[0])<(3-state[1]))):
return False
else:
return True
def generate_next_states(M,C,B):
moves = [[1, 0, 1], [0, 1, 1], [2, 0, 1], [0, 2, 1], [1, 1, 1]]
valid_states = []
for each in moves:
if(B==1):next_state = [x1 - x2 for (x1, x2) in zip([M, C, B], each)]
else:next_state = [x1 + x2 for (x1, x2) in zip([M, C, B], each)]
if (is_valid(next_state)):
# print(next_state)
valid_states.append(next_state)
return valid_states
solutions = []
def find_sol(M,C,B,visited):
if([M,C,B]==[0,0,0]):#everyne crossed successfully
# print("Solution reached, steps: ",visited+[[0,0,0]])
solutions.append(visited+[[0,0,0]])
return True
elif([M,C,B] in visited):#prevent looping
return False
else:
visited.append([M,C,B])
if(B==1):#boat is in left
for each_s in generate_next_states(M,C,B):
find_sol(each_s[0],each_s[1],each_s[2],visited[:])
else:#boat in in right
for each_s in generate_next_states(M,C,B):
find_sol(each_s[0],each_s[1],each_s[2],visited[:])
find_sol(3,3,1,[])
solutions.sort()
for each_sol in solutions:
print(each_sol)
Please refer to this gist to see a possible solution, maybe helpful to your problem.
Gist: solve Missionaries and cannibals in Prolog
I've solved with depth-first and then with breadth-first, attempting to clearly separate the reusable part from the state search algorithm:
miss_cann_dfs :-
initial(I),
solve_dfs(I, [I], Path),
maplist(writeln, Path), nl.
solve_dfs(S, RPath, Path) :-
final(S),
reverse(RPath, Path).
solve_dfs(S, SoFar, Path) :-
move(S, T),
\+ memberchk(T, SoFar),
solve_dfs(T, [T|SoFar], Path).
miss_cann_bfs :-
initial(I),
solve_bfs([[I]], Path),
maplist(writeln, Path), nl.
solve_bfs(Paths, Path) :-
extend(Paths, Extended),
( member(RPath, Extended),
RPath = [H|_],
final(H),
reverse(RPath, Path)
; solve_bfs(Extended, Path)
).
extend(Paths, Extended) :-
findall([Q,H|R],
( member([H|R], Paths),
move(H, Q),
\+ member(Q, R)
), Extended),
Extended \= [].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% problem representation
% independent from search method
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
initial((3,3, >, 0,0)).
final((0,0, <, 3,3)).
% apply a *valid* move
move((M1i,C1i, Bi, M2i,C2i), (M1f,C1f, Bf, M2f,C2f)) :-
direction(Bi, F1, F2, Bf),
who_move(MM, CM),
M1f is M1i + MM * F1, M1f >= 0,
C1f is C1i + CM * F1, C1f >= 0,
( M1f >= C1f ; M1f == 0 ),
M2f is M2i + MM * F2, M2f >= 0,
C2f is C2i + CM * F2, C2f >= 0,
( M2f >= C2f ; M2f == 0 ).
direction(>, -1, +1, <).
direction(<, +1, -1, >).
% valid placements on boat
who_move(M, C) :-
M = 2, C = 0 ;
M = 1, C = 0 ;
M = 1, C = 1 ;
M = 0, C = 2 ;
M = 0, C = 1 .
I suggest you to structure your code in a similar way, with a predicate similar to extend/2, that make clear when to stop the search.
If your Prolog system has a forward chainer you can also solve
the problem by modelling it via forward chaining rules. Here
is an example how to solve a water jug problem in Jekejeke Minlog.
The state is represented by a predicate state/2.
You first give a rule that filters duplicates as follows. The
rule says that an incoming state/2 fact should be removed,
if it is already in the forward store:
% avoid duplicate state
unit &:- &- state(X,Y) && state(X,Y), !.
Then you give rules that state that search need not be continued
when a final state is reached. In the present example we check
that one of the vessels contains 1 liter of water:
% halt for final states
unit &:- state(_,1), !.
unit &:- state(1,_), !.
As a next step one models the state transitions as forward chaining
rules. This is straight forward. We model emptying, filling and pouring
of vessels:
% emptying a vessel
state(0,X) &:- state(_,X).
state(X,0) &:- state(X,_).
% filling a vessel
state(5,X) &:- state(_,X).
state(X,7) &:- state(X,_).
% pouring water from one vessel to the other vessel
state(Z,T) &:- state(X,Y), Z is min(5,X+Y), T is max(0,X+Y-5).
state(T,Z) &:- state(X,Y), Z is min(7,X+Y), T is max(0,X+Y-7).
We can now use the forward chaining engine to do the job for us. It
will not do iterative deeping and it will also not do breadth first.
It will just do unit resolution by a strategy that is greedy for the
given fact and the process only completes, since the state space
is finite. Here is the result:
?- post(state(0,0)), posted.
state(0, 0).
state(5, 0).
state(5, 7).
state(0, 7).
Etc..
The approach will tell you whether there is a solution, but not explain
the solution. One approach to make it explainable is to use a fact
state/4 instead of a fact state/2. The last two arguments are used for
a list of actions and for the length of the list.
The rule that avoids duplicates is then changed for a rule that picks
the smallest solution. It reads as follows:
% choose shorter path
unit &:- &- state(X,Y,_,N) && state(X,Y,_,M), M<N, !.
unit &:- state(X,Y,_,N) && &- state(X,Y,_,M), N<M.
We then get:
?- post(state(0,0,[],0)), posted.
state(0, 0, [], 0).
state(5, 0, [fl], 1).
state(5, 7, [fr,fl], 2).
state(0, 5, [plr,fl], 2).
Etc..
With a little helper predicate we can force an explanation of
the actions that lead to a path:
?- post(state(0,0,[],0)), state(1,7,L,_), explain(L).
0-0
fill left vessel
5-0
pour left vessel into right vessel
0-5
fill left vessel
5-5
pour left vessel into right vessel
3-7
empty right vessel
3-0
pour left vessel into right vessel
0-3
fill left vessel
5-3
pour left vessel into right vessel
1-7
Bye
Source Code: Water Jug State
http://www.xlog.ch/jekejeke/forward/jugs3.p
Source Code: Water Jug State and Path
http://www.xlog.ch/jekejeke/forward/jugs3path.p