Element-wise operations: Difference between revisions

From Rosetta Code
Content deleted Content added
Added PicoLisp
→‎{{header|Python}}: Add scalar-matrix computations.
Line 211: Line 211:
>>> pp(ewise(a0, a1, lambda x, y:2*x - y))
>>> pp(ewise(a0, a1, lambda x, y:2*x - y))
[[10, 11, 13, 2], [2, 10, 5, -2], [2, 4, 6, 5]]
[[10, 11, 13, 2], [2, 10, 5, -2], [2, 4, 6, 5]]
>>>
>>> def s_ewise(scalar1, matrix1, op):
return [[op(scalar1, e1) for e1 in row1] for row1 in matrix1]

>>> scalar = 10
>>> a0
[[7, 8, 7, 4], [4, 9, 4, 1], [2, 3, 6, 4]]
>>> for op in ( add, sub, mul, floordiv, pow, lambda x, y:2*x - y ):
print("%10s :" % op.__name__, s_ewise(scalar, a0, op))

add : [[17, 18, 17, 14], [14, 19, 14, 11], [12, 13, 16, 14]]
sub : [[3, 2, 3, 6], [6, 1, 6, 9], [8, 7, 4, 6]]
mul : [[70, 80, 70, 40], [40, 90, 40, 10], [20, 30, 60, 40]]
floordiv : [[1, 1, 1, 2], [2, 1, 2, 10], [5, 3, 1, 2]]
pow : [[10000000, 100000000, 10000000, 10000], [10000, 1000000000, 10000, 10], [100, 1000, 1000000, 10000]]
<lambda> : [[13, 12, 13, 16], [16, 11, 16, 19], [18, 17, 14, 16]]
>>> </lang>
>>> </lang>



Revision as of 12:30, 12 June 2011

Task
Element-wise operations
You are encouraged to solve this task according to the task description, using any language you may know.

Similar to Matrix multiplication and Matrix transposition, the task is to implement basic element-wise matrix-matrix and scalar-matrix operations, which can be referred to in other, higher-order tasks. Implement addition, subtraction, multiplication, division and exponentiation.

Extend the task if necessary to include additional basic operations, which should not require their own specialised task. A reference implementation in Common Lisp is included.

Common Lisp

Element-wise matrix-matrix operations. Matrices are represented as 2D-arrays. <lang lisp>(defun element-wise-matrix (fn A B)

 (let* ((len (array-total-size A))
        (m   (car (array-dimensions A)))
        (n   (cadr (array-dimensions A)))
        (C   (make-array `(,m ,n) :initial-element 0.0d0)))
   
   (loop for i from 0 to (1- len) do
        (setf (row-major-aref C i) 
              (funcall fn
                       (row-major-aref A i)
                       (row-major-aref B i))))
   C))
A.+B, A.-B, A.*B, A./B, A.^B.

(defun m+ (A B) (element-wise-matrix #'+ A B)) (defun m- (A B) (element-wise-matrix #'- A B)) (defun m* (A B) (element-wise-matrix #'* A B)) (defun m/ (A B) (element-wise-matrix #'/ A B)) (defun m^ (A B) (element-wise-matrix #'expt A B))</lang>

Elementwise scalar-matrix operations. <lang lisp>(defun element-wise-scalar (fn A c)

 (let* ((len (array-total-size A))
        (m   (car (array-dimensions A)))
        (n   (cadr (array-dimensions A)))
        (B   (make-array `(,m ,n) :initial-element 0.0d0)))
   
   (loop for i from 0 to (1- len) do
        (setf (row-major-aref B i) 
              (funcall fn
                       (row-major-aref A i)
                       c)))
   B))
c.+A, A.-c, c.*A, A./c, A.^c.

(defun .+ (c A) (element-wise-scalar #'+ A c)) (defun .- (A c) (element-wise-scalar #'- A c)) (defun .* (c A) (element-wise-scalar #'* A c)) (defun ./ (A c) (element-wise-scalar #'/ A c)) (defun .^ (A c) (element-wise-scalar #'expt A c))</lang>

D

The vector power operator is not enabled yet: <lang d>import std.stdio;

T[][] elementwiseMat(string op, T, U)(T[][] A, U B) if (is(U == T) || is(U == T[][])) {

   static if (is(U == T[][]))
       assert(A.length == B.length);
   if (!A.length)
       return null;
   auto R = new typeof(return)(A.length, A[0].length);
   foreach (r, row; A)
       static if (is(U == T)) {
           R[r][] = mixin("row[] " ~ op ~ "B");
       } else {
           assert(row.length == B[r].length);
           R[r][] = mixin("row[] " ~ op ~ "B[r][]");
       }
   return R;

}

void main() {

   auto scalar = 10;
   auto matrix = [[7, 11, 13], [17, 19, 23], [29, 31, 37]];
   writeln(elementwiseMat!q{ + }(matrix, scalar));
   writeln(elementwiseMat!q{ - }(matrix, scalar));
   writeln(elementwiseMat!q{ * }(matrix, scalar));
   writeln(elementwiseMat!q{ / }(matrix, scalar));
   //writeln(elementwiseMat!q{ ^^ }(matrix, scalar));
   writeln();
   writeln(elementwiseMat!q{ + }(matrix, matrix));
   writeln(elementwiseMat!q{ - }(matrix, matrix));
   writeln(elementwiseMat!q{ * }(matrix, matrix));
   writeln(elementwiseMat!q{ / }(matrix, matrix));
   //writeln(elementwiseMat!q{ ^^ }(matrix, matrix));

}</lang> Output:

[[17, 21, 23], [27, 29, 33], [39, 41, 47]]
[[-3, 1, 3], [7, 9, 13], [19, 21, 27]]
[[70, 110, 130], [170, 190, 230], [290, 310, 370]]
[[0, 1, 1], [1, 1, 2], [2, 3, 3]]

[[14, 22, 26], [34, 38, 46], [58, 62, 74]]
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
[[49, 121, 169], [289, 361, 529], [841, 961, 1369]]
[[1, 1, 1], [1, 1, 1], [1, 1, 1]]

J

Solution: J's arithmetical primitives act elementwise by default (in J parlance, such operations are known as "scalar" or "rank zero", which means they generalize to high-order arrays transparently, operating elementwise). Thus: <lang j> scalar =: 10

  vector =: 2 3 5
  matrix =: 3 3 $    7 11 13  17 19 23  29 31 37
  scalar * scalar

100

  scalar * vector

20 30 50

  scalar * matrix
70 110 130

170 190 230 290 310 370

  vector * vector

4 9 25

  vector * matrix
14  22  26
51  57  69

145 155 185

  matrix * matrix
49 121  169

289 361 529 841 961 1369</lang> And similarly for +, -, % (division), and ^ .

PARI/GP

GP already implements element-wise matrix-matrix addition and subtraction and element-wise scalar-matrix multiplication and division. Other element-wise matrix-matrix functions: <lang parigp>multMM(A,B)=matrix(#A[,1],#A,i,j,A[i,j]*B[i,j]); divMM(A,B)=matrix(#A[,1],#A,i,j,A[i,j]/B[i,j]); powMM(A,B)=matrix(#A[,1],#A,i,j,A[i,j]^B[i,j]);</lang>

Other element-wise scalar-matrix functions: <lang parigp>addMs(A,s)=A+matrix(#A[,1],#A,i,j,s); subMs(A,s)=A-matrix(#A[,1],#A,i,j,s); powMs(A,s)=matrix(#A[,1],#A,i,j,A[i,j]^s);</lang>

PL/I

Any arithmetic operation can be applied to elements of arrays. These examples iillustrate element-by-element multiplication, but addition, subtraction, division, and exponentiation can also be written. <lang PL/I> declare (matrix(3,3), vector(3), scalar) fixed; declare (m(3,3), v(3) fixed;

m = scalar * matrix; m = vector * matrix; m = matrix * matrix;

v = scalar * vector; v = vector * vector; </lang>

PicoLisp

<lang PicoLisp>(de elementWiseMatrix (Fun Mat1 Mat2)

  (mapcar '((L1 L2) (mapcar Fun L1 L2)) Mat1 Mat2) )

(de elementWiseScalar (Fun Mat Scalar)

  (elementWiseMatrix Fun Mat (circ (circ Scalar))) )</lang>

Test:

(let (S 10  M '((7 11 13) (17 19 23) (29 31 37)))
   (println (elementWiseScalar + M S))
   (println (elementWiseScalar - M S))
   (println (elementWiseScalar * M S))
   (println (elementWiseScalar / M S))
   (println (elementWiseScalar ** M S))
   (prinl)
   (println (elementWiseMatrix + M M))
   (println (elementWiseMatrix - M M))
   (println (elementWiseMatrix * M M))
   (println (elementWiseMatrix / M M))
   (println (elementWiseMatrix ** M M)) )

Output:

((17 21 23) (27 29 33) (39 41 47))
((-3 1 3) (7 9 13) (19 21 27))
((70 110 130) (170 190 230) (290 310 370))
((0 1 1) (1 1 2) (2 3 3))
((282475249 25937424601 137858491849) (2015993900449 6131066257801 ...

((14 22 26) (34 38 46) (58 62 74))
((0 0 0) (0 0 0) (0 0 0))
((49 121 169) (289 361 529) (841 961 1369))
((1 1 1) (1 1 1) (1 1 1))
((823543 285311670611 302875106592253) (827240261886336764177 ...

Python

<lang python>>>> import random >>> from operator import add, sub, mul, floordiv >>> from pprint import pprint as pp >>> >>> def ewise(matrix1, matrix2, op): return [[op(e1,e2) for e1,e2 in zip(row1, row2)] for row1,row2 in zip(matrix1, matrix2)]

>>> m,n = 3,4 # array dimensions >>> a0 = [[random.randint(1,9) for y in range(n)] for x in range(m)] >>> a1 = [[random.randint(1,9) for y in range(n)] for x in range(m)] >>> pp(a0); pp(a1) [[7, 8, 7, 4], [4, 9, 4, 1], [2, 3, 6, 4]] [[4, 5, 1, 6], [6, 8, 3, 4], [2, 2, 6, 3]] >>> pp(ewise(a0, a1, add)) [[11, 13, 8, 10], [10, 17, 7, 5], [4, 5, 12, 7]] >>> pp(ewise(a0, a1, sub)) [[3, 3, 6, -2], [-2, 1, 1, -3], [0, 1, 0, 1]] >>> pp(ewise(a0, a1, mul)) [[28, 40, 7, 24], [24, 72, 12, 4], [4, 6, 36, 12]] >>> pp(ewise(a0, a1, floordiv)) [[1, 1, 7, 0], [0, 1, 1, 0], [1, 1, 1, 1]] >>> pp(ewise(a0, a1, pow)) [[2401, 32768, 7, 4096], [4096, 43046721, 64, 1], [4, 9, 46656, 64]] >>> pp(ewise(a0, a1, lambda x, y:2*x - y)) [[10, 11, 13, 2], [2, 10, 5, -2], [2, 4, 6, 5]] >>> >>> def s_ewise(scalar1, matrix1, op): return [[op(scalar1, e1) for e1 in row1] for row1 in matrix1]

>>> scalar = 10 >>> a0 [[7, 8, 7, 4], [4, 9, 4, 1], [2, 3, 6, 4]] >>> for op in ( add, sub, mul, floordiv, pow, lambda x, y:2*x - y ): print("%10s :" % op.__name__, s_ewise(scalar, a0, op))


      add : [[17, 18, 17, 14], [14, 19, 14, 11], [12, 13, 16, 14]]
      sub : [[3, 2, 3, 6], [6, 1, 6, 9], [8, 7, 4, 6]]
      mul : [[70, 80, 70, 40], [40, 90, 40, 10], [20, 30, 60, 40]]
 floordiv : [[1, 1, 1, 2], [2, 1, 2, 10], [5, 3, 1, 2]]
      pow : [[10000000, 100000000, 10000000, 10000], [10000, 1000000000, 10000, 10], [100, 1000, 1000000, 10000]]
 <lambda> : [[13, 12, 13, 16], [16, 11, 16, 19], [18, 17, 14, 16]]

>>> </lang>

Tcl

<lang tcl>package require Tcl 8.5 proc alias {name args} {uplevel 1 [list interp alias {} $name {} {*}$args]}

  1. Engine for elementwise operations between matrices

proc elementwiseMatMat {lambda A B} {

   set C {}
   foreach rA $A rB $B {

set rC {} foreach vA $rA vB $rB { lappend rC [apply $lambda $vA $vB] } lappend C $rC

   }
   return $C

}

  1. Lift some basic math ops

alias m+ elementwiseMatMat {{a b} {expr {$a+$b}}} alias m- elementwiseMatMat {{a b} {expr {$a-$b}}} alias m* elementwiseMatMat {{a b} {expr {$a*$b}}} alias m/ elementwiseMatMat {{a b} {expr {$a/$b}}} alias m** elementwiseMatMat {{a b} {expr {$a**$b}}}

  1. Engine for elementwise operations between a matrix and a scalar

proc elementwiseMatSca {lambda A b} {

   set C {}
   foreach rA $A {

set rC {} foreach vA $rA { lappend rC [apply $lambda $vA $b] } lappend C $rC

   }
   return $C

}

  1. Lift some basic math ops

alias .+ elementwiseMatSca {{a b} {expr {$a+$b}}} alias .- elementwiseMatSca {{a b} {expr {$a-$b}}} alias .* elementwiseMatSca {{a b} {expr {$a*$b}}} alias ./ elementwiseMatSca {{a b} {expr {$a/$b}}} alias .** elementwiseMatSca {{a b} {expr {$a**$b}}}</lang>