OpBinary

c = op(alpha1 * a + alpha2 * b) + beta * c;

struct OpBinary (
T
size_t dim
string ops
) if (
isFloatingPoint!T
) {
T alpha1;
T alpha2;
uint[dim] shape1;
uint[dim] shape2;
Variable!(T, dim, HostStorage) ha;
Variable!(T, dim, HostStorage) hb;
enum opBinaryDict;
Variable!(T, dim, DeviceStorage) da;
Variable!(T, dim, DeviceStorage) db;
}

Examples

1 static foreach (op; ["+", "*"]) {
2     foreach (j; [1, 2]) {
3         import std.typecons : tuple;
4         import numir : uniform, approxEqual;
5         import mir.ndslice : slice;
6         import grain.testing;
7 
8         auto a = uniform!float(3, 2).slice.variable;
9         auto b = uniform!float(3, j).slice.variable;
10         auto gc = uniform!float(3, 2).slice.variable;
11         auto func = OpBinary!(float, 2, op)(1, 2);
12         gradCheck(func, tuple(a, b), gc);
13 
14         auto c = func.forward(a, b);
15         auto gab = func.backward(gc);
16         version (grain_cuda) {
17             auto dfunc = OpBinary!(float, 2, op)(1, 2);
18             auto dc = dfunc.forward(a.to!DeviceStorage, b.to!DeviceStorage);
19             assert(approxEqual(dc.to!HostStorage.sliced, c.sliced));
20             auto dgab = dfunc.backward(gc.to!DeviceStorage);
21             assert(approxEqual(dgab[0].to!HostStorage.sliced, gab[0].sliced));
22             assert(approxEqual(dgab[1].to!HostStorage.sliced, gab[1].sliced));
23         }
24     }
25 }
1 foreach (i; [1, 2]) {
2     foreach (j; [1, 2]) {
3         import std.typecons : tuple;
4         import numir : uniform;
5         import mir.ndslice : slice;
6         import grain.testing;
7 
8         auto a = uniform!float(i, 2).slice.variable;
9         auto b = uniform!float(2, j).slice.variable;
10         auto gc = uniform!float(2, 2).slice.variable;
11         auto func = OpBinary!(float, 2, "*")(1, 2);
12         gradCheck(func, tuple(a, b), gc);
13     }
14 }

a and b have the same shape

1 import mir.ndslice;
2 
3 auto plus = OpBinary!(float, 2, "+")(1.0f, 2.0f);
4 auto a = [[1.0f, 2.0f, 3.0f], [4.0f, 5.0f, 3.0f]].variable;
5 auto b = [[-1.0f, 4.0f, 0.0f], [1.0f, 2.0f, 3.0f]].variable;
6 auto hc = plus.forward(a, b);
7 assert(hc.sliced == [[-1.0f, 10.0f, 3.0f], [6.0f, 9.0f, 9.0f]]);
8 
9 version (grain_cuda) {
10     auto dplus = OpBinary!(float, 2, "+")(1.0f, 2.0f);
11     auto dc = dplus.forward(a.to!DeviceStorage, b.to!DeviceStorage);
12     assert(dc.to!HostStorage.sliced == [[-1.0f, 10.0f, 3.0f], [6.0f, 9.0f, 9.0f]]);
13 }

a and b have different shapes

1 import mir.ndslice;
2 
3 auto plus = OpBinary!(float, 2, "+")(1.0f, 2.0f);
4 auto a = [[1.0f, 2.0f, 3.0f], [4.0f, 5.0f, 3.0f]].variable;
5 auto b = [[-1.0f, 4.0f, 0.0f]].variable;
6 auto hc = plus.forward(a, b);
7 assert(hc.sliced == [[-1.0f, 10.0f, 3.0f], [2.0f, 13.0f, 3.0f]]);
8 
9 version (grain_cuda) {
10     auto dc = plus.forward(a.to!DeviceStorage, b.to!DeviceStorage);
11     assert(dc.to!HostStorage.sliced == [[-1.0f, 10.0f, 3.0f], [2.0f, 13.0f, 3.0f]]);
12 }

a and b have different shapes

1 import mir.ndslice;
2 
3 auto plus = OpBinary!(float, 2, "*")(1.0f, 2.0f);
4 auto a = [[1.0f, 2.0f, 3.0f], [4.0f, 5.0f, 3.0f]].variable;
5 auto b = [[-1.0f, 4.0f, 0.0f]].variable;
6 auto hc = plus.forward(a, b);
7 assert(hc.sliced == [[1*2*-1, 2*2*4, 0], [4*2*-1, 5*2*4, 0]]);
8 
9 version (grain_cuda) {
10     auto dc = plus.forward(a.to!DeviceStorage, b.to!DeviceStorage);
11     assert(dc.to!HostStorage.sliced ==[[1*2*-1, 2*2*4, 0], [4*2*-1, 5*2*4, 0]]);
12 }

Meta