Saul Garcia
Saul Garcia

Reputation: 940

Matrix multiplication with Prolog

I have to write a predicate the predicate product/3 which receives two matrix and returns the matrix multiplication of them if possible or fail otherwise. (This means if the matrices fullfill the requirement [n x p] [p x y], then return the multiplication with dimensions [n x y])

Example:

product(M1, M2, R)
    ?- product([[1,2],[3,4],[5,6]], [[1,1,1],[1,1,1]], M).
    M = [[3, 3, 3], [7, 7, 7], [11, 11, 11]];
    No

For this I have two codes that index the nth row on a matrix rowI and that index the nth column columnI (I explain how they work in the code below).

%Predicate: rowI(M, I, RI)
%Input      rowI([[1,2],[3,4],[5,6]], 2, RI).
%           RI = [3,4];

rowI([H|_],1,H):-!.
rowI([_|T],I,X) :-
    I1 is I-1,
    rowI(T,I1,X).

%        columnJ(M, J, CJ)
%Input   columnJ([[1,2],[3,4],[5,6]], 1, CJ).
%        CJ = [1,3,5];

columnJ([],_,[]).
columnJ([H|T], I, [R|X]):-
    rowI(H, I, R), 
    columnJ(T,I,X).


product([H|T], M2, [R|X]):-

    columnJ(M2, C, Z),
    mult(H, Z , X),
    product(T, M2 , X).

I was thinking somehow by grabbing the head of the M1 (which will be each row) and then multiplied for each column in M2 and after adding the multiplication this list will be the new row. So (C would have to be a counter starting from 1 to the length of M2and then mult I was just thinking on having it multiplying the lists. (mult is not defined at this point, just a guess).

Here I am trying to explain the way I am thinking it.. but there may be a simplier way. What do you think?

Upvotes: 2

Views: 2561

Answers (1)

CapelliC
CapelliC

Reputation: 60014

Compact code (with the help of higher order constructs maplist and foldl). I left on purpose the expressions unevaluated, so the result could be reused in more general context:

:- module(matrix_multiply,
    [matrix_multiply/3
    ,dot_product/3
    ]).
:- use_module(library(clpfd), [transpose/2]).

%%  matrix_multiply(+X,+Y,-M) is det.
%
%   X(N*P),Y(P*M),M(N*M)
%
matrix_multiply(X,Y,M) :-
    transpose(Y,T),
    maplist(row_multiply(T),X,M).

row_multiply(T,X,M) :-
    maplist(dot_product(X),T,M).

dot_product([X|Xs],[T|Ts],M) :-
    foldl(mul,Xs,Ts,X*T,M).
mul(X,T,M,M+X*T).

edit

usage (save in a file named matrix_multiply.pl):

?- [matrix_multiply].
?- matrix_multiply([[1,2],[3,4],[5,6]], [[1,1,1],[1,1,1]],R),maplist(maplist(is),C,R).
R = [[1*1+2*1, 1*1+2*1, 1*1+2*1], [3*1+4*1, 3*1+4*1, 3*1+4*1], [5*1+6*1, 5*1+6*1, 5*1+6*1]],
C = [[3, 3, 3], [7, 7, 7], [11, 11, 11]].

The numeric evaluation is explicitly requested by ,maplist(maplist(is),C,R). R holds the symbolic expressions, C the values.

edit

Just to note that dependency from clpfd:transpose is easy to remove: here is an alternative 'one-liner' definition based on nth/3 and library(yall)

mat_transpose([R1|Rs],T) :- findall(V,(
    nth1(Col,R1,_),
    maplist({Col}/[R,C]>>nth1(Col,R,C),[R1|Rs],V)),T).

Upvotes: 4

Related Questions