So i did quick experimentation with elementwise operator improvements:

(1) stuff like 1 + exp (M):

(1a): this requires generalization in optimizer for elementwise unary
operators. I've added things like notion if operators require non-zero
iteration only or not.

(1b): added fusion of elemntwise operators, i.e.
   ew(1+, ew(exp, A)) is rewritten as ew (1+exp, A) for performance
reasons. It still uses an application of a fold over functional monoid, but
i think it should be fairly ok performance/DSL trade-off here. to get it
even better, we may add functional assignment syntax to distributed
operands similar to in-memory types as descrbed further down.

(1c): notion that self elementwise things such as expr1 * expr1 (which is
surprisingly often ocurrence, e..g in Torgerson MDS) are rewritten as ew(A,
square) etc.

So that much works. (Note that this also obsoletes dedicated scalar/matrix
elementwise operators that there currently are). Good.


The problem here is that (of course!) semantics of the scala language has
problem importing something like exp(Double):Double alongside with
exp(DRM):DRM apparently because it doesn't adhere to overloading rules
(different results) so in practice even though it is allowed, one import
overshadows the other.

Which means, for the sake of DSL we can't have exp(matrix), we have to name
it something else. Unless you see a better solution.

So ... elementwise naming options:

Matrix: mexp(m), msqrt(m). msignum(m)
Vector: vexp(v), vsqrt(v)...
DRM: dexp(drm), dsqrt(drm) .... ?

Let me know what you think.

(2) Another problem is that actually doing something like 1+exp(m) on
Matrix or Vector types is pretty impractical since, unlike in R (that can
count number of bound variables to an object) the semantics requires
creating a clone of m for something like exp(m) to guarantee no side
effects on m itself.

That is, expression 1 + exp(m) for Matrix or vector types causes 2
clone-copies of original argument.

actually that's why i use in-place syntax for in-memory types quite often,
something like

   1+=: (x *= x)  instead of more naturally looking 1+ x * x.


But unlike with simple elementwise operators (+=), there's no in-place
modification syntax for a function. We could put an additional parameter,
something like

mexp(m, inPlace=true) but i don't like it too much.

What i like much more is functional assignment (we already have assignment
to a function (row, col, x) => Double but we can add elementwise function
assignment) so that it really looks like

m := exp

That is pretty cool.
Except there's a problem of optimality of assignment.

There are functions here (e.g. abs, sqrt) that don't require full iteration
but rather non-zero iteration only. by default notation

m := func

implies dense iteration.

So what i suggest here is add a new syntax to do sparse iteration
functional assignments:

m ::= abs

I actually like it (a lot) because it short and because it allows for more
complex formulas in the same traversal, e.g. proverbial R's exp(m)+1
in-place will look

m := (1 + exp(_))

So not terrible.

What it lacks though is automatic determination of composite function need
to apply to all vs. non-zeros only for in-memory types (for distributed
types optimizer tracks this automatically).

i.e.

m := abs

is not optimal (because abs doesn't affect 0s) and

m ::= (abs(_) + 1)

is probably also not what one wants (when we have composition of dense and
sparse affecting functions, result is dense affecting function).

So here thoughts are also welcome. (I am inclining to go with ::= and :=
operators because functional assignment of complex expressions is the only
well performing strategy i see here for in-memory types).

Reply via email to