Element-wise operations: Difference between revisions

jq
No edit summary
(jq)
Line 692:
 
Note that in some branches of mathematics, it has been traditional to define multiplication such that it is not performed element-wise. This can introduce some complications ([[wp:Einstein notation]] is arguably the best approach for resolving those complexities in latex, when they occur frequently enough that mentioning and using the notation is not more complicated than explicitly describing the multiply-and-sum) and makes expressing element-wise multiplication complicated. J deals with this conflict by making its multiplication primitive be elementwise (consistent with the rest of the language) and by using a different verb (typically +/ .*) to represent the traditional non-element-wise multiply and sum operation.
 
=={{header|jq}}==
The following definition of elementwise allows matrices of any type
to be processed, e.g. the matrices could be string or object-valued,
and they can be of mixed type.
 
The matrices also need not be rectangular or conformant,
but the resultant matrix will be rectangular, with the same number
of rows as self, and if that number is greater than 0, then the number of
columns in the result will be the length of the first row of self.
 
In jq, it is idiomatic to specify an operation by using a jq filter.
This means that composite and user-defined operations can be
specified. In the following definition of "elementwise", the
"operator" argument for addition, for example, would be given as
(.[0] + .[1]) rather than the string "+".
 
In Part 2 below, a variation of "elementwise" is presented that does
accept string specifications of common operators, e.g. "+" for addition.
However this is done mainly for illustration and is not recommended,
primarily because it introduces certain complexities.
 
'''Part 1'''
<lang jq># Occurrences of .[0] in "operator" will refer to an element in self,
# and occurrences of .[1] will refer to the corresponding element in other.
def elementwise( operator; other ):
length as $rows
| if $rows == 0 then .
else . as $self
| other as $other
| ($self[0]|length) as $cols
| reduce range(0; $rows) as $i
([]; reduce range(0; $cols) as $j
(.; .[$i][$j] = ([$self[$i][$j], $other[$i][$j]] | operator) ) )
end ;</lang>
'''Example''':
<lang jq>[[3,1,4],[1,5,9]] as $m1 | [[2,7,1],[8,2,2]] as $m2
| ( ($m1|elementwise(.[0] + .[1]; $m2) ),
($m1|elementwise(.[0] + 2 * .[1]; $m2) ),
($m1|elementwise(.[0] < .[1]; $m2) ) )
</lang>
{{Out}}
<lang sh>[[5,8,5],[9,7,11]]
[[7,15,6],[17,9,13]]
[[false,true,false],[true,false,false]]
</lang>
 
'''Part 2'''
 
In elementwise2, the operator can be any jq filter e.g. (.[0] <
.[1]), where .[0] refers to an element in self and .[1] to the
corresponding element in other, but if it is one of the strings "+",
"-", "*", "/", "%", "//", "**", "^" or "pow", then the corresponding
operator will be applied. Note that in jq, operators are in general
polymorphic. For example, + is defined on strings and other types
besides numbers.
<lang jq>def elementwise2( operator; other ):
def pow(i): . as $in | reduce range(0;i) as $i (1; .*$in);
def operation(x; op; y):
[x,y] | op as $op
| if $op == "+" then x+y
elif $op == "-" then x-y
elif $op == "*" then x*y
elif $op == "/" then x/y
elif $op == "%" then x%y
elif $op == "//" then x/y|floor
elif $op == "**" or $op == "^" or $op == "pow" then x|pow(y)
else $op
end;
 
length as $rows
| if $rows == 0 then .
else . as $self
| other as $other
| ($self[0]|length) as $cols
| reduce range(0; $rows) as $i
([]; reduce range(0; $cols) as $j
(.; .[$i][$j] = operation($self[$i][$j]; operator; $other[$i][$j] ) ) )
end;</lang>
'''Example''':
<lang jq>[[3,1,4],[1,5,9]] as $m1 | [[2,7,1],[8,2,2]] as $m2
| ( ($m1|elementwise2("+"; $m2) ),
($m1|elementwise2("//"; $m2)),
($m1|elementwise2(.[0] < .[1]; $m2) ) )</lang>
{{Out}}
<lang sh>[[5,8,5],[9,7,11]]
[[1,0,4],[0,2,4]]
[[false,true,false],[true,false,false]]
</lang>
 
=={{header|K}}==
2,515

edits