store grain.autograd.BackProp object in returned variables from forward function
type-erased version of backward function used in grain.autograd.BackProp object
static foreach (op; ["+", "*"]) { foreach (j; [1, 2]) { import std.typecons : tuple; import numir : uniform, approxEqual; import mir.ndslice : slice; import grain.testing; auto a = uniform!float(3, 2).slice.variable; auto b = uniform!float(3, j).slice.variable; auto gc = uniform!float(3, 2).slice.variable; auto func = OpBinary!(float, 2, op)(1, 2); gradCheck(func, tuple(a, b), gc); auto c = func.forward(a, b); auto gab = func.backward(gc); version (grain_cuda) { auto dfunc = OpBinary!(float, 2, op)(1, 2); auto dc = dfunc.forward(a.to!DeviceStorage, b.to!DeviceStorage); assert(approxEqual(dc.to!HostStorage.sliced, c.sliced)); auto dgab = dfunc.backward(gc.to!DeviceStorage); assert(approxEqual(dgab[0].to!HostStorage.sliced, gab[0].sliced)); assert(approxEqual(dgab[1].to!HostStorage.sliced, gab[1].sliced)); } } }
foreach (i; [1, 2]) { foreach (j; [1, 2]) { import std.typecons : tuple; import numir : uniform; import mir.ndslice : slice; import grain.testing; auto a = uniform!float(i, 2).slice.variable; auto b = uniform!float(2, j).slice.variable; auto gc = uniform!float(2, 2).slice.variable; auto func = OpBinary!(float, 2, "*")(1, 2); gradCheck(func, tuple(a, b), gc); } }
a and b have the same shape
import mir.ndslice; auto plus = OpBinary!(float, 2, "+")(1.0f, 2.0f); auto a = [[1.0f, 2.0f, 3.0f], [4.0f, 5.0f, 3.0f]].variable; auto b = [[-1.0f, 4.0f, 0.0f], [1.0f, 2.0f, 3.0f]].variable; auto hc = plus.forward(a, b); assert(hc.sliced == [[-1.0f, 10.0f, 3.0f], [6.0f, 9.0f, 9.0f]]); version (grain_cuda) { auto dplus = OpBinary!(float, 2, "+")(1.0f, 2.0f); auto dc = dplus.forward(a.to!DeviceStorage, b.to!DeviceStorage); assert(dc.to!HostStorage.sliced == [[-1.0f, 10.0f, 3.0f], [6.0f, 9.0f, 9.0f]]); }
a and b have different shapes
import mir.ndslice; auto plus = OpBinary!(float, 2, "+")(1.0f, 2.0f); auto a = [[1.0f, 2.0f, 3.0f], [4.0f, 5.0f, 3.0f]].variable; auto b = [[-1.0f, 4.0f, 0.0f]].variable; auto hc = plus.forward(a, b); assert(hc.sliced == [[-1.0f, 10.0f, 3.0f], [2.0f, 13.0f, 3.0f]]); version (grain_cuda) { auto dc = plus.forward(a.to!DeviceStorage, b.to!DeviceStorage); assert(dc.to!HostStorage.sliced == [[-1.0f, 10.0f, 3.0f], [2.0f, 13.0f, 3.0f]]); }
a and b have different shapes
import mir.ndslice; auto plus = OpBinary!(float, 2, "*")(1.0f, 2.0f); auto a = [[1.0f, 2.0f, 3.0f], [4.0f, 5.0f, 3.0f]].variable; auto b = [[-1.0f, 4.0f, 0.0f]].variable; auto hc = plus.forward(a, b); assert(hc.sliced == [[1*2*-1, 2*2*4, 0], [4*2*-1, 5*2*4, 0]]); version (grain_cuda) { auto dc = plus.forward(a.to!DeviceStorage, b.to!DeviceStorage); assert(dc.to!HostStorage.sliced ==[[1*2*-1, 2*2*4, 0], [4*2*-1, 5*2*4, 0]]); }
c = op(alpha1 * a + alpha2 * b) + beta * c;